TS中的泛型(generics)

目录

什么是泛型

为什么要有泛型

示例代码

泛型函数

泛型类

泛型的继承

泛型去继承一个类

泛型去继承一个接口


什么是泛型

在 TypeScript 中,泛型(Generics)是一种允许在定义类、接口和函数时,延迟指定其中某些类型的概念。泛型可以使代码更具有通用性和灵活性,因为它可以用于处理多种类型的数据。

泛型通过使用泛型参数(Generic parameter)来实现。泛型参数使用尖括号(<>)括起来放在类型的名称后面。这个参数可以在类、接口或函数内部代表任何类型。

为什么要有泛型

泛型在编程中扮演着重要的角色,主要有以下几个原因:

1. 提高代码的灵活性和复用性:泛型允许我们编写通用的代码,能够适应多种数据类型。通过使用泛型,我们可以在类、接口和函数中定义使用参数化类型的逻辑,从而避免相同的代码重复编写。

2. 类型安全性:在使用泛型的情况下,编译器可以在编译时进行类型检查和推断。这意味着编译器可以确保数据的一致性以及正确的类型使用,减少程序在运行时发生类型错误的可能性。

3. 提高代码的可读性和可维护性:通过使用泛型,我们可以使代码更加清晰、易读和可维护。泛型参数可以提供有意义的类型名称,并且在代码中使用泛型的地方可以直观地表达意图。

4. 适应不同的数据类型:在处理不同类型的数据时,使用泛型可以确保代码的适应性和通用性。无论是处理字符串、数字、对象还是其他自定义类型,泛型可以在不修改代码的情况下适应这些类型的变化。

5. 避免类型断言和类型转换:使用泛型可以避免手动进行类型转换和类型断言的繁琐操作。泛型提供了类型推断和类型约束的功能,使得代码更加简洁和可维护。

总的来说,泛型使得我们可以编写更加灵活、可复用和类型安全的代码。它不仅提高了开发效率,还减少了错误和调试的成本。因此,在需要处理多种数据类型的情况下,使用泛型是非常有价值的。

示例代码

function save(a:number):number {
    return a;
}

let s = save(12)
// 现在这个函数功能比较单一,只能存数字,但是如果我们想可以让这个函数既能存数组又能存字符串,那么这个时候,我们首先想到的联合类型
function save(a:number|string):number|string {
    return a;
}
let ss:number|string = save(12)
let sss:number|string = save('stt')
//如果用联合类型,那么我现在还想存Date、Biolean、自定义的User类型。那么咱们这个联合写不下去了。
// 所以咱么就想到一个比较极端的方法,我让这个函数的参数是一个any类型。
function save(a:any):any {
    return a;
}
let b = save(12);
let c = save('str')
let d:number = save('ghirgh') // 也能过。any就相当于没有类型,一般不用any

正确写法,应该把类型当成参数传进去。也就是咱们一开始的时候,确定不了这个类型,但是用的时候,能够确定

// T只是一个占位符,可以用任何字母去表示
function save<T>(a:T):T {
  return a;
}

let s:string = save<number>(12) // 编译不过,因为在调用这个函数的时候,传递了一个number,但是s是string类型的。
let ss = save('str'); // 可以通过类型推测,推测出是一个string类型
let sss = save(true)

泛型函数

泛型函数其实就是在函数后面加上<T>,T可以用任何类型都代替

// 泛型函数
function joinArray<T>(...args:T[][]):T[] {
  let result:T[] = [];
  args.forEach(arr => {
    result = result.concat(arr)
  })
  return result;
}
// [1, 2, 3, 'a']
let arr = joinArray<number| string>([1, 2, 3], ['a', 'b', 'c'])
console.log(arr)

泛型类

class Box<T> {
  private value: T;

  constructor(value: T) {
    this.value = value;
  }

  getValue(): T {
    return this.value;
  }
}

const numberBox = new Box<number>(42);
console.log(numberBox.getValue()); // 输出: 42

const stringBox = new Box<string>('Hello');
console.log(stringBox.getValue()); // 输出: 'Hello'

泛型的继承

泛型去继承一个类
  • 当泛型去继承一个类的时候,并不是代表这个类具有了泛型继承的这个类的所有属性和方法。而是在用这个类的时候,接收的参数,只能是当前类及其子类
class Box<T> {
  value: T;

  constructor(value: T) {
    this.value = value;
  }

  getValue(): T {
    return this.value;
  }
}

class NumberBox<T extends number> extends Box<T> {
  add(other: NumberBox<T>): NumberBox<T> {
    const sum = this.value + other.getValue();
    return new NumberBox<T>(sum as T);
  }

  subtract(other: NumberBox<T>): NumberBox<T> {
    const diff = this.value - other.getValue();
    return new NumberBox<T>(diff as T);
  }
}

const number1 = new NumberBox<number>(42);
const number2 = new NumberBox<number>(10);

const result1 = number1.add(number2);
console.log(result1.getValue()); // 输出: 52

const result2 = number1.subtract(number2);
console.log(result2.getValue()); // 输出: 32

Box 类是一个泛型基类,NumberBox 类继承自 Box 类,并扩展了泛型参数的范围。NumberBox 类仅接受类型为 number 的泛型参数,因此在 add 和 subtract 方法中使用泛型参数 T 时,编译器可以确定 T 的实际类型,从而可以保证运算的正确性。

需要注意的是,当定义扩展泛型基类的类时,需要在类的名称后添加泛型参数的范围约束,以确保子类的类型参数满足基类的泛型参数约束。例如,在上述示例中,NumberBox 类继承自 Box<number> 类型,泛型参数的范围约束为 T extends number,表示子类的泛型参数必须是 number 类型或其子类型。

泛型去继承一个接口
interface Printable {
  print(): void;
}

function printItem<T extends Printable>(item: T): void {
  item.print();
}

class Book implements Printable {
  print(): void {
    console.log("Printing book...");
  }
}

class Magazine implements Printable {
  print(): void {
    console.log("Printing magazine...");
  }
}

printItem(new Book());     // 输出: Printing book...
printItem(new Magazine()); // 输出: Printing magazine...

我们定义了一个名为 Printable 的接口,该接口包含一个 print 方法。然后,我们使用泛型继承了 Printable 接口,并在 printItem 函数中使用泛型参数 T 扩展了 Printable 接口。这样,我们可以确保传递给 printItem 函数的参数满足 Printable 接口的要求。

在函数调用中,我们分别传递了 Book 和 Magazine 类的实例作为参数。由于 Book 和 Magazine 类都实现了 Printable 接口,且满足 T extends Printable 泛型约束,所以它们可以传递给 printItem 函数,并且能够调用 print 方法进行打印操作。

通过泛型继承接口,我们可以在函数中对泛型类型进行更加精确的约束,以确保类型的一致性和兼容性。

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TypeScript泛型可以让我们编写可重用的代码组件,这些组件可以支持多种类型。泛型TypeScript 的一个强大特性,它可以在函数、类、接口使用。 泛型的基本语法是在函数名或类名后加上 "<T>"(T可以是任何标识符),这个T表示类型变量,它可以代表任意类型。例如: ```typescript function identity<T>(arg: T): T { return arg; } let output = identity<string>("hello world"); console.log(output); // 输出 hello world ``` 在上面的例子,我们定义了一个名为 identity 的函数,它接收一个参数 arg,并返回该参数。在函数名后面加上了 "<T>",这样我们就可以在函数使用类型变量 T,代表任意类型。 调用 identity 函数时,我们可以明确指定 T 的类型,例如:identity<string>("hello world"),这表示 T 的类型是 string。也可以不指定 T 的类型,TypeScript 会根据传入的参数自动推断出 T 的类型,例如:identity("hello world"),TypeScript 会自动将 T 推断为 string。 泛型还可以用于类和接口,例如: ```typescript interface GenericIdentityFn { <T>(arg: T): T; } class Identity<T> { private value: T; constructor(value: T) { this.value = value; } getValue(): T { return this.value; } } let myIdentityFn: GenericIdentityFn = identity; let myIdentity = new Identity<string>("hello"); console.log(myIdentity.getValue()); // 输出 hello ``` 在上面的例子,我们定义了一个名为 GenericIdentityFn 的接口,它定义了一个泛型函数。我们还定义了一个名为 Identity 的类,该类接收一个类型参数 T,并存储一个类型为 T 的值。 通过泛型,我们可以编写可重用的代码组件,它可以支持多种类型,提高代码的复用性和灵活性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值