TS类型编程的乐趣-泛型

272 阅读3分钟

TypeScript-泛型

(——泛指的类型,泛型就像是类型层面的函数,可通过抽象、封装类型运算逻辑实现类型可复用)

1、定义

📍泛型:(概念)

泛型指的是类型参数化,将原来某种具体的类型进行参数化, 和定义函数参数一样,我们可以给泛型定义若干个类型参数,并在调用时给泛型传入明确的类型参数。(参考JAVA)

设计泛型的目的在于有效约束类型成员之间的关系,比如函数参数和返回值、类或者接口成员和方法之间的关系。(面试)

📍泛型类型:(类型)

将类型入参的定义移动到类型别名或接口名称后,此时定义的一个接收具体类型入参后返回一个新类型的类型就是泛型类型。

type A<T> = (param: T) => T; // 泛型类型A

interface B<T> {   // 泛型类型B
  (param: T): T;
}

2、场景

  1. 泛型函数:用于约束函数参数的类型;(常用)
  2. 泛型类:用于约束的构造函数、属性、方法参数返回值的类型;
💡 对于React,组件也支持泛型(类式组件、函数组件)
function GenericCom<P>(props: { prop1: string }) {
return <></>;
};
<GenericCom<{ name: string; }> prop1="1" ... />

3、用途

  1. 解决函数类型不能是固定的,可以是改变的类型;
  2. 解决除了使用any以外,函数类型不固定且参数和返回值类型需要一致;

4、使用

  • 泛型约束extends关键字,把函数或者接口泛型入参约束在特定的范围内的子集

    // 泛型入参名 extends 类型
    function A <T extends number | string | boolean>(param: T):T {
      return param;
    }
    // 在多个不同的泛型入参之间设置约束依赖关系
    interface B {
      <O extends {}, K extends keyof O, V extends O[K]>(obj: O, key: K, value: V): V; 
    }
    
  • 泛型入参指定默认值(即默认类型)

    interface C <T = { id: number; name: string }> {
      state: T
    }
    
  • 泛型约束和泛型默认值一起使用

    function A <T extends number | string | boolean = number>(param: T):T {
      return param;
    }
    

💡注意:

  1. 枚举类型不支持泛型;

  2. 函数的泛型入参必须和参数/参数成员建立有效的约束关系才有实际意义,仅约束返回值类型的泛型,是没有任何意义的;同时泛型入参的类型(可缺省)可以根据函数参数类型自动进行推断;

  3. 分配条件类型:在条件类型判断的情况下,如果入参是联合类型,则会被拆解成一个个独立的(原子)类型(成员)进行类型运算;(只有泛型 + extends + 三元,才会触发)

        type A<T> = T extends string | number ? T[] : T;
        type B = string | boolean;
    
        // 分配条件类型
        type C = A<B>; // => boolean | string[]
    
        // 不是泛型入参 不触发分配条件类型
        type D = B extends string | number ? B[] : B; // boolean | string
    
        // 注意:never是不能分配的底层类型,即never以原子形式作为泛型入参并出现在extends左侧,则直接返回never类型
        type StringOrNumberArray<E> = E extends string | number ? E : E[];
        type GetSNums = never extends number ? number[] : never extends string ? string[] : never; // number[];
        type GetNever = StringOrNumberArray<never>; // never   extends左侧为never 直接返回never
    
        type UselessNeverZ<T> = [T] extends [{}] ? T[] : [];   // 可以使用 [] 进行包裹当成整体
        type ThisIsNotNeverZ = UselessNeverZ<never>; // never[]   不是以原子形式被 extends 使用
    
    
  4. 注意:

    • 泛型是函数运行时推断出的类型;
    • 不要给泛型类型的形参设置默认值;
    • 若非设置默认值不可, 只能断言泛型;
    // 泛型为不确定的类型,不能将一个固定类型赋值给泛型类型,否则报错
    function toString<T extends number>(x: T = 1): T {   // ts(2322) 不能一起设置默认值
      return 2   // ts(2322) 可以返回参数x
    }