索引类型(keyof)
function pluck(obj, keys) {
return keys.map(key => obj[key])
}
上面这个函数的目的是通过遍历 keys 来获取 obj 里面的值,最后返回包含所有值的数组,我们希望 keys 的所有 key 都是 obj 拥有的,如果要使用 ts 来做类型约束的话,用常规方法很难实现,这时就可以使用索引类型来实现:
function pluck<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
return keys.map(key => obj[key])
}
interface Person {
name: string
age: number
}
let person: Person = {
name: 'Jarid',
age: 35
}
let strings = pluck(person, ['name', 'age'])
我们通过泛型及keyof来实现类型约束的,keyof就是索引类型查询操作符,keyof T就是获取泛型T的所有属性名的集合,在上面例子中就是'name' | 'age',这样我们就获取到了 Person 上所有 key 组成的联合类型。T[K]是索引访问,获取T所有存在于K的属性的类型组成的联合类型。
映射类型(in)
type Keys = 'name' | 'hobby'
type Person = { [K in Keys]: string }
ts 中 in 的用法和 for...in 类似,会对 Keys 进行遍历赋值,然后返回结果,上面这个例子得到的结果就是:js
type Person = {
name: string
hobby: string
}
我们可以结合索引类型和映射类型来实现一些工具类泛型,例如实现一个将传入类型的所有属性设置成可选属性,然后返回处理后的类型:
type Optional<T> = {
[P in keyof T]?: T[P]
}
type Search = {
name: string
age: number
}
type OptionalSearch = Optional<Person>
[P in keyof T]就是遍历T中的 key,T[P]就是当前 key 的类型,然后我们在遍历的 key 后面加上?就是将这个属性变成可选属性,这样我们在调用这个泛型的时候,就会将传入的类型的所有属性变成可选属性并返回处理好后的新类型。
辅助泛型
结合索引类型和映射类型能实现很多很方便实用的泛型,其实在 TS 已经内置了一些常用的辅助泛型。
Partial(可选)
/**
* Make all properties in T optional
*/
type Partial<T> = {
[P in keyof T]?: T[P]
}
Partial<T>将T的所有属性变成可选的:
[P in keyof T]通过映射类型,遍历T上所有的属性?:将属性设置为可选的T[P]通过索引访问将类型设置为原来的类型
Required(必选)
/**
* Make all properties in T required
*/
type Required<T> = {
[P in keyof T]-?: T[P]
}
这个泛型的作用和Partial刚好相反,是将传入类型的所有属性变成必选的,-?的意思就是将属性的?去掉,这样该属性自然就变成必选属性了。
Readonly(只读)
/**
* Make all properties in T readonly
*/
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
Readonly<T>将T的所有属性变成只读的
Exclude(排除)
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T
Exclude<T, U>从T中排除存在于U的类型后组成的联合类型
- 通过遍历
T中的所有子类型,如果该类型存在于U,则返回never,否则返回该子类型,最终得到的便是T排除了U中所有类型后的联合类型。 never表示一个不存在的类型,与其他类型联合后,never会被去除掉type E = Exclude<'a' | 'b' | 'd', 'b' | 'c'>,结果为:type E = 'a' | 'd'
Extract(提取)
/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never
Extract<T, U>提取同时存在于联合类型T和U的类型组成的联合类型
这个泛型的作用和Exclude刚好相反,例如type E = Extract<'a' | 'b' | 'd', 'b' | 'c'>,这次我们得到的结果是type E = 'b'
Pick(筛选)
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
Pick<T, K extends keyof T>从T筛选存在于K的类型
K extends keyof TK的类型必须存在于T中[P in K]遍历K的所有类型T[P]通过索引访问将类型设置为原来的类型
Omit(过滤)
/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
Omit<keyof T, K>就是从T中将K中所有属性过滤
Omit是结合Pice和Exclude实现的,效果和Pick相反- 先通过
Exclude<keyof T, K>从T的索引类型集合中排除K的所有类型 - 然后通过
Pick从T中筛选出排除后的所有类型Example:
interface Coder {
id: string,
name: string,
age: number,
hobby: string[]
}
const JSer: Omit<Coder, 'name' | 'age'> = {
id: '1001',
hobby: ["code", "羽毛球"]
}
Record(记录)
/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
[P in K]: T;
}
Record<K extends keyof any, T>遍历K的每一项属性,类型设置为T
Example:
interface Obj {
title: string,
value: number
}
const List: Record<string, Obj> = {
home: { title: '首页', value: 1 },
search: { title: '搜索', value: 2 }
}
ReturnType(返回值类型)
/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any) => any> = T extends (
...args: any
) => infer R
? R
: any
ReturnType<T extends (...args: any) => any> 返回传入函数类型T的返回值类型
T extends (...args: any) => any约束参数T必须是函数类型infer关键词的作用是让TS自己推导类型,并将推导结果存储在infer后面的参数类型上infer关键词只能在extends条件类型上使用- 这里的实现方式就是先判断
T是否是函数类型并推导返回值类型存储在R上,是则返回R,不是则返回never