泛型编程(上)
泛型就像函数
函数
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