在阅读本文之前,请先阅读TypeScript解决了JavaScript的什么问题?,需要在你脑海中建立这样一个概念:TypeScript是一门语言,既然是一门语言,它就可以编程,只是它的编程对象是类型,不是值,即类型编程实际也是编程。
下面来实现一个简单的函数:key 是 obj 的属性名。
js
function pickValue(obj, key) {
return obj[key];
}
如果要为其进行类型定义的话,有哪些需要定义的地方?
需要定义的有:参数 obj、参数 key、返回值。
同时,这三者有存在一些关联关系,比如:key 必然是 obj 中的键值名之一,一定为string
类型;返回的值一定是obj中的键值。
js
function pickValue<T>(obj: T, key: keyof T) {
return obj[key]
}
keyof
keyof
是 索引类型查询的语法,它返回由 obj 的属性名组成的字面量联合类型。
js
interface foo {
a: number;
b: string;
}
type A = keyof foo; // "a" | "b"
上面标注了参数,但是没有标注返回值,那返回值怎么标注呢?
我们拿到了key,就能拿到对应的value,那么value的类型也就不在话下了:
js
function pickValue<T>(obj: T, key: keyof T): T[keyof T] {
return obj[key]
}
伪代码解释下:
js
interface T {
a: number;
b: string;
}
type TKeys = keyof T; // "a" | "b"
type PropAType = T['a']; // number
如果你能深刻认识到 TypeScript 是一门语言且能编程的话,上面就好理解了,它无非操作的就是类型而已。T[keyof T]
就能很好的说明这一特性。
但这种写法很明显有可以改进的地方:keyof
出现了两次,以及泛型T应该被限制为对象类型,就像我们平时会做的那样:用一个变量把多处出现的存起来,在类型编程里,泛型就是变量。
js
function pickValue<T extends object, U extends keyof T>(obj: T, key: U): T[U] {
return obj[key]
}
extends
extends
是撒东西呢?
你可以把T extends object
理解为T被限制为对象类型 ,U extends keyof T
理解为泛型U必然是泛型T的属性名组成的联合类型(以字面量类型的形式) 。
假设现在我们不只要取出一个值了,我们要取出一系列值:
js
function pickValue<T extends object, U extends keyof T>(
obj: T,
keys: U[]
): T[U][] {
return keys.map(key => obj[key])
}
这里有两个变化:
keys: U[]
:U是T的属性名组成的联合类型,那么要表示一个内部元素均是T的属性名的数组,就可以使用这种方式。T[U][]
它的原理实际上和上面一条相同,之所以单独拿出来是因为我认为它是一个很好地例子:简单的表现了TS类型编程的组合性,你不感觉这种写法就像搭积木一样吗?
映射类型 Mapped Types
映射类型同样是类型编程的重要底层组成,通常用于在旧有类型的基础上进行改造,包括接口包含字段、字段的类型、修饰符(readonly与?)等等。
从一个简单场景入手:
js
interface IUser {
name: string
title: string
age: number
isMan: boolean
}
现在我们有个需求,实现一个接口,它的字段与接口A完全相同,但是其中的类型全部为string,你会怎么做?直接重新声明一个然后手写吗?
这真是太笨了。
如果把接口换成对象再想想,其实很简单,new一个新对象,然后遍历A的键名(Object.keys()
)来填充这个对象。
js
type IUserStringObj<T> = {
[K in keyof T]: string
}
是不是很熟悉这个in
操作符,你完全可以把它理解为就是for...in
,也就是说你可以通过in
遍历到联合类型里面的每个类型,比如利用它还可以复制一个接口:
js
type Clone<T> = {
[K in keyof T]: T[K]
}
掌握这种思路,其实你已经接触到一些工具类型的底层实现了。比如Partial
工具类型:
js
type Partial<T> = {
[K in keyof T]?: T[K]
}
是不是特别简单。