泛型编程(上)
泛型就像函数
函数
1 | const f = (a, b) => a + b |
泛型
1 | type F<A, B> = A | B |

函数的本质是什么
函数的本质是推后执行的、部分待定的代码
1 | // 立即执行 |
泛型的本质是什么
泛型的本质是推后执行的、部分待定的类型
没有泛型的类型系统,就如同没有函数的编程语言
什么时候需要泛型
当我们需要准确的定义返回值的类型的时候,就需要泛型

这时候你可能会说,做类型收窄不就行了吗?就像下面这样
1 | function echo(whatever: number | string | boolean) { |
但是你会发现,这样写得到的返回值类型是不准确的
1 | const a = echo(233); |
这时候,泛型就派上用场了
1 | function echo<T>(whatever: T): T { |
简单泛型示例
1 | type Union<A, B> = A | B; |
条件类型(Conditional Types)

T extends string 可理解为 T <= string(T 包含于 string)
1 | type R1 = LikeString<'hi'> // true |
泛型中的特殊运算规则
现有如下泛型 ToArray
1 | type ToArray<T> = T extends unknown ? T[] : never; |
问:type Result = ToArray<string | number> 的类型是什么?
直接说答案:Result 为 string[] | number[]
你可能会疑惑,按照一般的理解,string | number 是包含于 unknown 的,那么 Result 就应该是 (string | number)[] 呀
按照通常的理解是没错,但因为这是在泛型中,因此规则会有些许不同
可以按照如下拆分过程来进行理解记忆:
1 | type Result = ToArray<string | number>; |
即泛型中的联合类型会分开进行运算(extends 运算),这就好像是乘法中的分配率 (A + B) X C = A X C + B X C
再问:type Result2 = ToArray<never> 的类型是什么?
答案是 never
同样按照一般的理解,通常情况下(非泛型中),never 是包含于 unknown 的,那么 Result 应该是 never[] 才对
但 Result 在此处却为 never
1 | type Result = ToArray<never>; // never |
即泛型中的 never 进行任何运算(extends 运算或者 & 运算)都只会得到 never,这就好像是乘法中的零 0 X C = 0
注意:以上讨论的规则只对泛型有效
在非泛型中,使用 extends 运算会得到不一样的结果
1 | // 非泛型 |
那么,如何才能禁用泛型中的自动拆分规则呢?
我们可以构造一个类型,在外面包裹上一个新的类型来防止 TypeScript 遍历 extends 左侧类型的行为,比如下面的 []
1 | type IsNever<T> = [T] extends [never] ? true : false |
在泛型中使用 keyof
keyof 关键字用于获取对象类型的所有键的联合类型
1 | type Person = { name: string; age: number }; |
常和映射类型一起使用
1 | type Readonly<T> = { |
keyof 也可用于数组 / 元组
1 | type A = keyof [1, 2, 3]; |
在泛型中使用 extends keyof
1 | type Person = { name: string; age: number }; |
K extends keyof T 的写法称为泛型约束,GetKeyType 第二个参数 k 的类型必须包含于 string | number
其他
PropertyKey
PropertyKey 是 TS 中的一个内置类型,它表示可以用作对象属性键的类型。它是 string | number | symbol 类型的别名,因为这些类型都可以用作对象属性的键。
PropertyKey 类型常用于泛型约束,以确保泛型类型参数只能是可以用作对象属性键的类型。例如,定义一个泛型类型 Foo<K extends PropertyKey>,其中 K 只能是 string、number 或 symbol 类型。
T[number]
1 | type A = [1, 2, 3] |
A[number] 表示获取类型 A 的数字索引签名的类型。由于元组类型具有隐式的数字索引签名,因此 A[number] 的类型为元组中所有元素类型的联合类型 1 | 2 | 3