什么是泛型?
关于什么是泛型这个问题不是太好回答,比如在面试中,如果有候选人反过来问我这个问题,可能我也给不出一个特别标准的答案。
不过,我们可以借用 Java 中泛型的释义来回答这个问题:泛型指的是类型参数化,即将原来某种具体的类型进行参数化。和定义函数参数一样,我们可以给泛型定义若干个类型参数,并在调用时给泛型传入明确的类型参数。设计泛型的目的在于有效约束类型成员之间的关系,比如函数参数和返回值、类或者接口成员和方法之间的关系。
泛型类型参数
function reflect(param: unknown) {return param;}const str = reflect('string'); // str 类型是 unknownconst num = reflect(1); // num 类型 unknown
reflect 函数虽然可以接收一个任意类型的参数并原封不动地返回参数的值,不过返回值类型不符合我们的预期。
我们可以通过尖括号 <> 语法给函数定义一个泛型参数 P,并指定 param 参数的类型为 P ,如下代码所示:
function reflect<P>(param: P) {return param;}const reflectStr = reflect<string>('string'); // reflectStr 类型是 stringconst reflectNum = reflect<number>(1); // reflectNum 类型 number
泛型不仅可以约束函数整个参数的类型,还可以约束参数属性、成员的类型,比如参数的类型可以是数组、对象,如下示例:
function reflectArray<P>(param: P[]) {return param;}const reflectArr = reflectArray([1, '1']); // reflectArr 是 (string | number)[]
extends
typescript 2.8引入了条件类型关键字: extends,长这个样子:
T extends U ? X : Y
用大白话可以表示为:
如果T包含的类型 是 U包含的类型的 ‘子集’,那么取结果X,否则取结果Y。
再举几个ts预定义条件类型的例子,加深理解:
type NonNullable<T> = T extends null | undefined ? never : T;// 如果泛型参数 T 为 null 或 undefined,那么取 never,否则直接返回T。let demo1: NonNullable<number>; // => numberlet demo2: NonNullable<string>; // => stringlet demo3: NonNullable<undefined | null>; // => never
分配式extends
T extends U ? X : Y
其实就是当上面的T为联合类型的时候,会进行拆分,有点类似数学中的分解因式:
(a + b) * c => ac + bc
再举个官网的例子:
type Diff<T, U> = T extends U ? never : T; // 找出T的差集type Filter<T, U> = T extends U ? T : never; // 找出交集type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // => "b" | "d"// <"a" | "b" | "c" | "d", "a" | "c" | "f">// 相当于// <'a', "a" | "c" | "f"> |// <'b', "a" | "c" | "f"> |// <'c', "a" | "c" | "f"> |// <'d', "a" | "c" | "f">type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // => "a" | "c"// <"a" | "b" | "c" | "d", "a" | "c" | "f"> 同上let demo1: Diff<number, string>; // => number
infer
Infer 关键字用于条件中的类型推导。
Typescript 官网也拿 ReturnType 这一经典例子说明它的作用:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
理解为:如果 T 继承了 extends (…args: any[]) => any 类型,则返回类型 R,否则返回 any。其中 R 是什么呢?R 被定义在 extends (…args: any[]) => infer R 中,即 R 是从传入参数类型中推导出来的。
/* _____________ 你的代码 _____________ */type First<T extends any[]> = T extends [infer K, ...infer S] ? K : never/* _____________ 测试用例 _____________ */import type { Equal, Expect } from '@type-challenges/utils'type cases = [Expect<Equal<First<[3, 2, 1]>, 3>>,Expect<Equal<First<[() => 123, { a: string }]>, () => 123>>,Expect<Equal<First<[]>, never>>,Expect<Equal<First<[undefined]>, undefined>>,]type errors = [// @ts-expect-errorFirst<'notArray'>,// @ts-expect-errorFirst<{ 0: 'arrayLike' }>,]
