共计 7537 个字符,预计需要花费 19 分钟才能阅读完成。
前言
- 本文将简要介绍一些工具泛型应用及其实现, 这些泛型接口定义大多数是语法糖 (简写), 你能够在 typescript 包中的 lib.es5.d.ts 中找到它的定义, 咱们我的项目的版本 “typescript”: “^3.9.7″,
关键字
在理解这这些内置帮忙类型之前,咱们先聊一聊一些关键字,有助于理解,因为这些关键字和 js 中的意识还是有出入的,我过后就一脸懵逼
extends
- 能够用来继承一个 class,interface, 还能够用来判断有条件类型 (很多时候在 ts 看到 extends,并不是继承的意识)
- 示例:
T extends U ? X : Y;
- 下面的类型意思是,若 T 可能赋值给 U,那么类型是 X,否则为 Y。
原理是令 T’ 和 U’ 别离为 T 和 U 的实例,并将所有类型参数替换为 any,如果 T’ 能赋值给 U’,则将有条件的类型解析成 X,否则为 Y。
下面的官网解释有点绕,上面举个栗子:
type Words = 'a'|'b'|"c";
type W<T> = T extends Words ? true : false;
type WA = W<'a'>; // -> true
type WD = W<'d'>; // -> false
- a 能够赋值给 Words 类型,所以 WA 为 true,而 d 不能赋值给 Words 类型,所以 WD 为 false。
infer
- 示意在 extends 条件语句中待推断得类型变量 (可联合前面的 returnType)
type Union<T> = T extends Array<infer U> ? U: never
- 如果泛型参数 T 满足约束条件 Array<infer U> 那么就返回这个类型变量 U
- 有点懵逼再来一个
type ParamType<T> = T extends (param: infer P) => any ? P: T;
// 解析如果 T 能赋值给 (param: infer P) => any 类型,就返回 P,否则就返回 T
interface IDog {
name: string;
age:number;
}
type Func = (dog:IDog) => void;
type Param = ParamType<Func>; // IDog
type TypeString = ParamType<string> // string
keyof
- keyof 能够用来获得一个对象接口的所有 key 值:
- 示例:
interface IDog {
name: string;
age: number;
sex?: string;
}
type K1 = keyof Person; // "name" | "age" | "sex"
type K2 = keyof Person[]; // "length" | "push" | "pop" ...
type K3 = keyof {[x: string]: Person }; // string | number
typeof
- 在 JS 中 typeof 能够判断数据类型,在 TS 中,它还有一个作用,就是获取一个变量的申明类型,如果不存在,则获取该类型的推论类型。
- 示例:
interface IDog {
name: string;
age: number;
sex?: string;
}
const jack: IDog = {name: 'jack', age: 100};
type Jack = typeof jack; // -> IDog
function foo(x: number): Array<number> {return [x];
}
type F = typeof foo; // -> (x: number) => number[]
- Jack 这个类型别名实际上就是 jack 的类型 Person,而 F 的类型就是 TS 本人推导进去的 foo 的类型 (x: number) => number[]。
内置帮忙类型
Partial
/**
* Make all properties in T optional
* 让 T 中的所有属性都是可选的
*/
type Partial<T> = {[P in keyof T]?: T[P];
};
- 在某些状况下,咱们心愿类型中的所有属性都不是必须的,只有在某些条件下才存在,咱们就能够应用 Partial 来将已申明的类型中的所有属性标识为可选的。
- 示例:
interface Dog {
age: number;
name: string;
price: number;
}
type PartialDog = Partial<Dog>;
// 等价于
type PartialDog = {
age?: number;
name?: string;
price?: number;
}
let dog: PartialDog = {
age: 2,
name: 'xiaobai'
};
- 在上述示例中因为咱们应用 Partial 将所有属性标识为可选的,因而最终 dog 对象中尽管只蕴含 age 和 name 属性,然而编译器仍旧没有报错,当咱们不能明确地确定对象中蕴含哪些属性时,咱们就能够通过 Partial 来申明。
Partial
/**
* Make all properties in T required
* 使 T 中的所有属性都是必须的
*/
type Required<T> = {[P in keyof T]-?: T[P];
};
- Required 的作用刚好跟 Partial 相同,Partial 是将所有属性改成可选项,Required 则是将所有类型改成必选项:
- 其中 -? 是代表移除 ? 这个 modifier 的标识。
- 与之对应的还有个 +? , 这个含意天然与 -? 之前相同, 它是用来把属性变成可选项的,+ 可省略,见 Partial。
- 示例:
interface Dog {
age: number;
name: string;
price: number;
}
type RequiredDog = Required<Dog>;
// 等价于
type RequiredDog = {
age: number;
name: string;
price: number;
}
let dog: RequiredDog = {
age?: 2,
name?: 'xiaobai'
};
Readonly
/**
* Make all properties in T readonly
* 将所有属性设置为只读
*/
type Readonly<T> = {readonly [P in keyof T]: T[P];
};
- 给子属性增加 readonly 的标识,如果将下面的 readonly 改成 -readonly,就是移除子属性的 readonly 标识。
- 示例:
interface IDog{
name: string;
age: number;
}
type TDog = Readonly<IDog>;
class TestDog {run() {
let dog: IDog = {
name: 'dd',
age: 1
};
person.name = 'cc';
let dog1: TDog = {
name: 'read',
age: 1
};
// person2.age = 3; 报错, 不能赋值
}
}
Pick
/**
* From T, pick a set of properties whose keys are in the union K
* 从 T 中,抉择一组键在并集 K 中的属性
*/
type Pick<T, K extends keyof T> = {[P in K]: T[P];
};
- 从源码能够看到 K 必须是 T 的 key,而后用 in 进行遍历, 将值赋给 P, 最初 T[P] 获得相应属性的值。
- 示例:
interface IDog {
name: string;
age: number;
height: number;
weight: number;
}
type PickDog = Pick<IDog, "name" | "age" | "height">;
// 等价于
type PickDog = {
name: string;
age: number;
height: number;
};
let dog: PickDog = {
name: 'wangcai',
age: 3,
height: 70
};
- 在上述示例中,因为咱们只关怀 IDog 对象中的 name,age 和 height 是否存在,因而咱们就能够应用 Pick 从 IDog 接口中拣选出咱们关怀的属性而疏忽其余属性的编译查看。
Record
/**
* Construct a type with a set of properties K of type T
* 结构一个具备一组属性 K(类型 T) 的类型
*/
type Record<K extends keyof any, T> = {[P in K]: T;
};
- 能够依据 K 中的所有可能值来设置 key,以及 value 的类型
-
示例:
let dog = Record<string, string | number | undefined>; // -> string | number | undefined
该类型能够将 K 中所有的属性的值转化为 T 类型,并将返回的新类型返回给 dog,K 能够是联结类型、对象、枚举…
- 示例:
type petsGroup = 'dog' | 'cat';
interface IPetInfo {
name:string,
age:number,
}
type IPets = Record<petsGroup, IPetInfo>;
const animalsInfo:IPets = {
dog:{
name:'wangcai',
age:2
},
cat:{
name:'xiaobai',
age:3
},
}
Exclude
/**
* Exclude from T those types that are assignable to U
* 从 T 中排除那些可调配给 U 的类型
*/
type Exclude<T, U> = T extends U ? never : T;
- 与 Pick 相同,Pick 用于拣选出咱们须要关怀的属性,而 Exclude 用于排除掉咱们不须要关怀的属性
- 示例:
interface IDog {
name: string;
age: number;
height: number;
weight: number;
sex: string;
}
type keys = keyof IDog; // -> "name" | "age" | "height" | "weight" | "sex"
type ExcludeDog = Exclude<keys, "name" | "age">;
// 等价于
type ExcludeDog = "height" | "weight" | "sex";
- 在上述示例中咱们通过在 ExcludeDog 中传入咱们只关怀的 height、weight、sex 属性,Exclude 会帮忙咱们将不须要的属性进行剔除。留下的属性 id,name 和 gender 即为咱们须要关怀的属性。
- 示例:
type T = Exclude<1 | 2, 1 | 3> // -> 2
- 很轻松地得出后果 2 依据代码和示例咱们能够推断出 Exclude 的作用是从 T 中找出 U 中没有的元素, 换种更加贴近语义的说法其实就是从 T 中排除 U
- 一般来说,Exclude 很少独自应用,能够与其余类型配合实现更简单更有用的性能。
Extract
/**
* Extract from T those types that are assignable to U
* 从 T 中提取可调配给 U 的类型
*/
type Extract<T, U> = T extends U ? T : never;
- Extract 的作用是提取出 T 蕴含在 U 中的元素,换种更加贴近语义的说法就是从 T 中提取出 U
- 以上语句的意思就是 如果 T 能赋值给 U 类型的话,那么就会返回 T 类型,否则返回 never,最终后果是将 T 和 U 中共有的属性提取进去
- 示例:
type test = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'|'g'>; // -> 'a' | 'c'
- 能够看到 T 是 ‘a’ | ‘b’ | ‘c’ | ‘d’,而后 U 是 ‘a’ | ‘c’ | ‘f’|’g’,返回的新类型就能够将 T 和 U 中共有的属性提取进去,也就是 ‘a’ | ‘c’ 了。
Omit
/**
* Construct a type with the properties of T except for those in type K.
* 结构一个除类型 K 之外的 T 属性的类型
*/
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
- 在上一个用法中,咱们应用 Exclude 来排除掉其余不须要的属性,然而在上述示例中的写法耦合度较高,当有其余类型也须要这样解决时,就必须再实现一遍雷同的逻辑,应用 Omit 能够防止这些问题,老版本 ts 未内置,TypeScript 3.5 曾经内置:
- 示例:
interface IDog {
name: string;
age: number;
height: number;
weight: number;
sex: string;
}
// 示意疏忽掉 User 接口中的 name 和 age 属性
type OmitDog = Omit<IDog, "name" | "age">;
// 等价于
type OmitDog = {
height: number;
weight: number;
sex: string;
};
let dog: OmitDog = {
height: 1,
weight: 'wangcai',
sex: 'boy'
};
- 在上述示例中,咱们须要疏忽掉 IDog 接口中的 name 和 age 属性,则只须要将接口名和属性传入 Omit 即可,对于其余类型也是如此,大大提高了类型的可扩大能力,不便复用
NonNullable
/**
* Exclude null and undefined from T
* 从 T 中排除 null 和 undefined
*/
type NonNullable<T> = T extends null | undefined ? never : T;
- 这个类型能够用来过滤类型中的 null 及 undefined 类型。
- 示例:
type test = string | number | null;
type test1 = NonNullable<test>; // -> string | number;
Parameters
/**
* Obtain the parameters of a function type in a tuple
* 在元组中获取构造函数类型的参数
*/
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
- 该类型能够取得函数的参数类型组成的元组类型。
- 示例:
function foo(x: number): Array<number> {return [x];
}
type P = Parameters<typeof foo>; // -> [number]
- 此时 P 的实在类型就是 foo 的参数组成的元组类型 [number]。
ConstructorParameters
/**
* Obtain the parameters of a constructor function type in a tuple
* 在元组中获取构造函数类型的参数
*/
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
- 该类型的作用是取得类的参数类型组成的元组类型
- 示例:
class Person {
private firstName: string;
private lastName: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
}
type P = ConstructorParameters<typeof Person>; // -> [string, string]
- 此时 P 就是 Person 中 constructor 的参数 firstName 和 lastName 的类型所组成的元组类型 [string, string]。
ReturnType
/**
* Obtain the return type of a function type
* 获取函数类型的返回类型
*/
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
- 该类型的作用是获取函数的返回类型。
- 其实这里的 infer R 就是申明一个变量来承载传入函数签名的返回值类型, 简略说就是用它取到函数返回值的类型不便之后应用
- 理论应用的话,就能够通过 ReturnType 拿到函数的返回类型
- 示例:
function foo(x: number): Array<number> {return [x];
}
type fn = ReturnType<typeof foo>; // -> number[]
InstanceType
/**
* Obtain the return type of a constructor function type
* 获取构造函数类型的返回类型
*/
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
- 该类型的作用是获取构造函数类型的实例类型。
class ConstructorType {
x = 0;
y = 0;
}
type test1 = InstanceType<typeof ConstructorType>; // ConstructorType
type test1 = InstanceType<any>; // any
ThisType
/**
* Marker for contextual 'this' type
* 上下文“this”类型的标记
*/
interface ThisType<T> {}
- 这个类型是用于指定上下文对象类型的。
- 这类型怎么用呢,举个例子:
interface Cat {
name: string;
age: number;
}
const obj: ThisType<Person> = {mimi() {this.name // string}
}
- 这样的话,就能够指定 obj 里的所有办法里的上下文对象改成 Person 这个类型了。
// 没有 ThisType 状况下
const dog = {wang() {console.log(this.age); // error,在 dog 中只有 wang 一个函数,不存在 a
}
}
// 应用 ThisType
const dog: {wang: any} & ThisType<{age: number}> = {wang() {console.log(this.wang) // error,因为没有在 ThisType 中定义
console.log(this.age); // ok
}
}
dog.wang // ok 失常调用
dog.age // error,在里面的话,就跟 ThisType 没有关系了, 这里就是没有定义 age 了
- 从下面的代码中能够看到,ThisType 的作用是:提醒其下所定义的函数,在函数 body 中,其调用者的类型是什么。
- 如果你也爱折腾、爱前端,能够关注我的微信公众号:前端要致力,咱们一起学习、一起交换、一起进阶
参考
- https://segmentfault.com/a/11…
- 深刻了解 typescript
正文完
发表至: typescript
2020-11-13