keyof
返回对象中所有key的联合类型。
keyof 对象
得到该对象中所有的key值
type Dog = { name: string, age: number };
type D = keyof Dog;// name | age
遇到索引签名
遇到索引签名返回所有索引类型。
type Doggy = { [y:string]: boolean };
type doggy = keyof Doggy;// string | number 因为js中对象属性类型默认转化为字符串
type Dog = { [y:number]: number };
type dog = keyof Dog; //type dog = number
type Doggy = { [y:string]: unknown, [x:number]: boolean};
type doggy = keyof Doggy; //type doggy = string | number
any
// type T = string | number | symbol
type T = keyof any;
非对象
返回其类型对应的对象的所有key
in 不可遍历对象
in右侧一般跟着联合类型,in可对联合类型进行迭代。
type Animals = 'pig' | 'cat' | 'dog'
type animals = {
[key in Animals]: string
}
// type animals = {
// pig: string; //第一次迭代
// cat: string; //第二次迭代
// dog: string; //第三次迭代
// }
type T2 = {
[key in any]: string
// string: string number: string Symbol: string
}
extends
接口继承
interface T1 {
name: string
}
interface T2 {
sex: number
}
// 多重继承,逗号隔开
interface T3 extends T1,T2 {
age: number
}
// 合法
const t3: T3 = {
name: 'xiaoming',
sex: 1,
age: 18
}
条件判断
extends前面的类型能够赋值给extends后面的类型,即左边操作符是否能满足右边操作符
// 示例2
interface A1 {
name: string
}
interface A2 {
name: string
age: number
}
// A的类型为string
type A = A2 extends A1 ? string : number
const a: A = 'this is string'
// 上面的示例中,A2比A1多了age属性,A1相比A2的限制更少,能满足A2,则A1类型的值可以赋值给A2类型的值,判断为真。
特例
对于使用extends关键字的条件类型(即上面的三元表达式类型),如果extends前面的参数是一个泛型类型,当传入该参数的是联合类型,则使用分配律计算最终的结果。分配律是指,将联合类型的联合项拆成单项,分别代入条件类型,然后将每个单项代入得到的结果再联合起来,得到最终的判断结果。
type A1 = 'x' extends 'x' ? string : number; // string
// 因为'x'并不能满足'y'
type A2 = 'x' | 'y' extends 'x' ? string : number; // number
type P<T> = T extends 'x' ? string : number;
type A3 = P<'x' | 'y'> // <'x'> ? string : number
// <'y'> ? string : number
// 结果为string | number
该例中,extends的前参为T,T是一个泛型参数。在A3的定义中,给T传入的是'x'和'y'的联合类型'x' | 'y',满足分配律,于是'x'和'y'被拆开,分别代入P<T>
P<'x' | 'y'> => P<'x'> | P<'y'>
'x'代入得到
'x' extends 'x' ? string : number => string
'y'代入得到
'y' extends 'x' ? string : number => number
然后将每一项代入得到的结果联合起来,得到string | number
总之,满足两个要点即可适用分配律:第一,参数是泛型类型,第二,代入参数的是联合类型
特殊的never
never是所有类型的子类型
// never是所有类型的子类型
type A1 = never extends 'x' ? string : number; // string
type P<T> = T extends 'x' ? string : number;
type A2 = P<never> // never
上面的示例中,A2和A1的结果竟然不一样,看起来never并不是一个联合类型,所以直接代入条件类型的定义即可,获取的结果应该和A1一直才对啊?
实际上,这里还是条件分配类型在起作用。never被认为是空的联合类型,也就是说,没有联合项的联合类型,所以还是满足上面的分配律,然而因为没有联合项可以分配,所以P<T>的表达式其实根本就没有执行,所以A2的定义也就类似于永远没有返回的函数一样,是never类型的。
防止条件判断中的分配
type P<T> = [T] extends ['x'] ? string : number;
type A1 = P<'x' | 'y'> // number
type A2 = P<never> // string
在条件判断类型的定义中,将泛型参数使用[]括起来,即可阻断条件判断类型的分配,此时,传入参数T的类型将被当做一个整体,不再分配。
Exclude排除
在 ts 中,我们能够使用 Exclude<T,U> 这个工具,帮助我们把 T 类型当中属于 U 类型的部分去除后得到一个新的类型,ts 已经自己提供了,使用方式如下:
type myType = Exclude<'a' | 'b' | 'c', 'a'>// 'b' | 'c'
源码
// 源码定义
type Exclude<T, U> = T extends U ? never : T;
Extract提取
将第二个参数的联合项从第一个参数的联合项中提取出来,当然,第二个参数可以含有第一个参数没有的项。
type Extract<T, U> = T extends U ? T : never
type A = Extract<'key1' | 'key2', 'key1'> // 'key1'
Pick 用于对象
原理
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type User = {
firstName: string,
lastName: string,
age: number
}
type UserName = Pick<User, "firstName" | "lastName">
现在,使用我们的新类型,UserName ,我们可以定义一个仅由firstName 和lastName 组成的变量。
let user:UserName = {
firstName: "John",
lastName: "Doe"
}
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是新增的一个辅助类型,它的作用主要是:以一个类型为基础支持剔除某些属性,然后返回一个新类型。
type Person = {
name: string;
age: string;
location: string;
};
type PersonWithoutLocation = Omit<Person, 'location'>;// {name: string, age: string}
type QuantumPerson = {
name: string;
age: string;
};
Record
Record<K, V>,K为定义key值类型,V为定义value类型。
使用
type keys = 'a' | 'b'
// type 或 interface 都一样
type values<T> = {
name?: T,
age?: T,
gender?: string
}
// 自定义 value 类型
type T1 = Record<keys, values<number | string>>
let obj:T1 = {
a: { name: 'h' },
b: { age: 18 }
}
// 固定 value 值
type T2 = Record<keys, 111>
let obj1:T2 = {
a: 111,
b: 111
}
源码
type Record<K extends any, V> = {
[P in k]: V
}
Partial
Partial将类型中所有选项变为可选。
export interface PartialContact= Partial<Contact>
PartialContact{
name?: string;
}
Partial深拷贝功能
ts提供的Partial只能作用于浅层,因此下面这个方法实现深层的。
type DeepPartial<T> = T extends Object ? { [P in keyof T]?: DeepPartial1<T[P]> } : T;
源码
type Partial<T> = {
[P in keyof T]?: T[P]
}
Required
Required将类型中所有选项变为必选。
export interface RequiredContact= Required<Contact>
RequiredContact{
name: string; // 姓
}
源码
type Required<T> {
// -?变为必选
[P in keyof T]-?: T[P];
}
Readonly
Readonly变为只读不可修改.
type User = {
name: string
age?: number
}
type T1 = Readonly<User>
// 简单说 T1 和 T2 是一模一样的
type T2 = {
readonly name: string
readonly age?: number
}
源码
type Readonly<T> {
rendonly [P in keyof T]: T[P];
}
把所有只读类型,全都变成非只读就只需要 -readonly 就行了
NonNullable联合类型
NonNullable<T>:作用是去掉 T 中的 null 和 undefined。T 为字面量/具体类型的联合类型,如果是对象类型是没有效果的。如下
type T1 = NonNullable<string | number | undefined>;
// type T1 = string | number
type T2 = NonNullable<string[] | null | undefined>;
// type T2 = string[]
type T3 = {
name: string
age: undefined
}
type T4 = NonNullable<T3> // 对象是不行的
type NonNullable<T> = T & {}
ObjNonNullable对象版
type ExcludeNon<T> = { [P in keyof T as T[P] extends null | undefined ? never : P] : T[P]};