TS学习笔记(五):泛型

谁践踏了优雅 2022-01-20 05:19 290阅读 0赞

泛型是指定一个表示类型的变量,用它来代替某个实际的类型用于编程,而后通过实际调用时传入或推导的类型来对其进行替换,以达到一段使用泛型程序可以实际适应不同类型的目的。为了实现泛型所要达到的目的,我们也可采用指定类型为 any 或者为每一种可能的类型都写一个方法(重载),但这严重违反抽象和复用代码的原则。所以在考虑可重用组件的时候,我们应该使用泛型。

泛型其实是 C# 和 Java 这种强类型语言中的一个特性,TypeScript 把这一特性引进到了弱类型语言 JavaScript 中,对于没接触过强类型语言的前端 coder,理解这个概念可能有点吃力,如果看不太懂,可以先去了解一下 Java 中的泛型,下面进入正题。

初识泛型

定义泛型:我们把要传入函数的参数类型设为一个类型变量 T ,它能够帮助我们捕获用户传入的类型,之后出现 T 的地方,都会被替换成用户传入的类型。

  1. function identity<T>(arg: T): T {
  2. return arg;
  3. }
  4. function getFirst<T>(arr: T[]): T {
  5. return arr[0];
  6. }
  7. console.log(identity(10)); // 10
  8. console.log(identity('TS')); // TS
  9. console.log(getFirst([1, 2, 3, 4])); // 1
  10. console.log(getFirst(['a', 'b', 'c'])); // a
  11. 复制代码

使用泛型:有两种使用泛型的方式,第一种是传入所有的参数,包括类型参数;第二种是不传类型参数,因为 TypeScript 的编译器会利用类型推论来确定参数的类型,推荐使用第二种。

  1. function identity<T>(arg: T): T {
  2. return arg;
  3. }
  4. function getFirst<T>(arr: T[]): T {
  5. return arr[0];
  6. }
  7. // 使用一:传入所有参数
  8. console.log(identity<number>(10)); // 10
  9. console.log(identity<string>('TS')); // TS
  10. console.log(getFirst<number>([1, 2, 3, 4])); // 1
  11. console.log(getFirst<string>(['a', 'b', 'c'])); // a
  12. // 使用二:不传参数类型
  13. console.log(identity(10)); // 10
  14. console.log(identity('TS')); // TS
  15. console.log(getFirst([1, 2, 3, 4])); // 1
  16. console.log(getFirst(['a', 'b', 'c'])); // a
  17. 复制代码

泛型类型

一个泛型函数的类型如下:

  1. <泛型变量名称>(参数1: 泛型变量, 参数2: 泛型变量, ...参数n: 泛型变量) => 泛型变量
  2. 复制代码

可以以对象字面量的形式来定义泛型函数(这更像是接口),如:

  1. let foo: { <T>(arg: T): void };
  2. foo = function <T>(arg: T): void {
  3. console.log(arg);
  4. }
  5. foo(13); // 13
  6. 复制代码

将上面的例子中的 { <T>(arg: T): void }改为接口,则有:

  1. interface IGeneric {
  2. <T>(arg: T): void
  3. }
  4. let foo: IGeneric = function <T>(arg: T): void {
  5. console.log(arg)
  6. }
  7. foo(13); // 13
  8. 复制代码

最后,接口中也可以使用泛型,这样子就锁定了代码里可以使用的类型,如:

  1. interface IGeneric<T> {
  2. (arg: T): void
  3. }
  4. function fn<T>(arg: T): void {
  5. console.log(arg);
  6. }
  7. let myFn: IGeneric<number> = fn;
  8. myFn(13); //13
  9. 复制代码

泛型类

除了泛型接口,我们还可以创建泛型类(但是无法创建泛型枚举、泛型命名空间),泛型类使用 <> 包围泛型类型变量,如:

  1. class Person<T> {
  2. love: T;
  3. say: (arg: T) => T;
  4. }
  5. let me = new Person<string>();
  6. me.love = 'TS';
  7. // me.love = 520; // ERROR
  8. me.say = function(love: string){
  9. return `my love is ${love}`;
  10. }
  11. console.log(me.say('TS')); // my love is TS
  12. 复制代码

注意:类有两部分,分为静态部分和实例部分,泛型类的类型指的是实例部分的类型,静态属性不能使用该泛型类型。

泛型约束

泛型可以通过 extends 一个接口来实现泛型约束,写法如:<泛型变量 extends 接口> ,例子:

  1. interface IArray {
  2. length: number
  3. }
  4. function logIndex<T extends IArray>(arg: T): void {
  5. for (let i = 0; i < arg.length; ++i) {
  6. console.log(i)
  7. }
  8. }
  9. let arr = [1, 2, 3]
  10. // logIndex<number>(arr) // 报错
  11. logIndex<number[]>(arr) // 允许
  12. logIndex(arr) // 自动类型推导,允许
  13. 复制代码

可以在泛型里使用类类型,如使用泛型创建工厂函数,需要引用构造函数的类类型,有:

  1. function create<T>(c: { new(): T }): T {
  2. return new c()
  3. }
  4. 复制代码

发表评论

表情:
评论列表 (有 0 条评论,290人围观)

还没有评论,来说两句吧...

相关阅读

    相关 TS

    TS泛型 泛型可以创建可重用的组件,一个组件可以支持多种类型,解决类,接口,方法的复用性,以及对不确定数据类型的使用 class MinCla<T>{

    相关 ts

    泛型 `在定义函数、接口或者类的时候、不需要先指定具体类型、而在用的时候再指定类型的一种特性。` 其实就是把类也当作变量(类是可变的,未知的)使用,把类放在<>尖括号

    相关 TS学习笔记):

    泛型是指定一个表示类型的变量,用它来代替某个实际的类型用于编程,而后通过实际调用时传入或推导的类型来对其进行替换,以达到一段使用泛型程序可以实际适应不同类型的目的。为了实现泛型

    相关 ()

    泛型委托(C\ 编程指南) 委托可以定义它自己的类型参数。 引用泛型委托的代码可以指定类型参数以创建封闭式构造类型,就像实例化泛型类或调用泛型方法一样,如以下示例中所示: