Typescript泛型帮助类型
Typescript泛型帮助类型
关键字
extends
- 可以用来集成一个
class
,interface
, 还可以用来判断有条件类型(很多时候在ts看到extends,并不是继承) - 示例:
1
T extends U ? X : Y;
- 上面的类型意思是,若
T
能够赋值给U
,那么类型是X
,否则为Y
。原理是令T’
和U’
分别为T
和U
的实例,并将所有类型参数替换为any
,如果T’能赋值给U’,则将有条件的类型解析成X
,否则为Y
。上面的官方解释有点绕,下面举个例子。1
2
3
4
5
6
7type Words = 'a'|'b'|"c";
type W<T> = T extends Words ? true : false;
type WA = W<'a'>; // -> true
type WD = W<'d'>; // -> falsea
可以复制给Words
类型,所以WA
为true,而d
不能赋值给Words
类型,所以WD为false。infer
- 表示在
extends
条件语句中待推断的类型变量(可结合后面的returnType)。1
type Union<T> = T extends Array<infer U> ? U: never
- 如果泛型参数
T
满足约束条件Array
那么就返回这个类型变量U
. - 举例如下:
1
2
3
4
5
6
7
8
9
10
11
12type 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> // stringkeyof
keyof
可以用来取得一个对象接口的所有key
值。- 举例:
1
2
3
4
5
6
7
8
9interface 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 | numbertypeof
- 在JavaScript中,
typeof
可以推断数据类型,在TypeScript中,它还有一个作用,就是获取一个变量的声明类型,如果不存在,则获取该类型的推论类型。 - 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15interface IPerson {
name: string;
age: number;
sex?: string;
}
const jack: IPerson = { name: 'jack', age: 100 };
type Jack = typeof jack; // -> IPerson
function foo(x: number): Array<number> {
return [x];
}
type F = typeof foo; // -> (x: number) => number[]
- Jack 这个类型别名实际上就是 jack 的类型 IPerson,而 F 的类型就是 TS 自己推导出来的 foo 的类型 (x: number) => number[]。
TypeScript内置帮助类型
Partial
1 | /** |
- 在某些情况下,我们希望类型中的所有属性都不是必需的,只有在某些条件下才存在,我们就可以使用
Partial
来将已声明的类型中的所有属性标识为可选的。 - 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18interface 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
来声明。Required
1
2
3
4
5
6
7/**
* Make all properties in T required
* 使T中的所有属性都是必需的
*/
type Required<T> = {
[P in keyof T]-?: T[P];
}; Required
的作用刚好跟Partial
相反,Partial
是将所有属性改成可选项,Required
则是将所有类型改成必选项:其中-?
是代表移除?
这个modifier的标识。与之对应的还有个+?
, 这个含义自然与-?
之前相反, 它是用来把属性变成可选项的,+
可省略,见Partial
。Readonly
1
2
3
4
5
6
7/**
* Make all properties in T readonly
* 将所有属性设置为只读
*/
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};- 给子属性添加
readonly
的标识,如果将上面的readonly
改成-readonly
,就是移除子属性的readonly
标识。 - 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19interface 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
1
2
3
4
5
6
7/**
* 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]
去的相应属性的值。 - 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20interface 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
1
2
3
4
5
6
7
8/**
* 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
的类型。 - 示例:
1
let dog = Record<string, string | number | undefined>; // -> string | number | undefined
- 可以根据
K
中所有的属性的值转化为T
类型,并将返回的新类型返回给dog
,K
可以是联合类型、对象、枚举…… - 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18type 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
1 | /** |
- 与
Pick
相反,Pick
用于拣选出我们需要关心的属性,而Exclude
用于排除掉我们不需要关心的属性。 - 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13interface 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
即为我们需要关心的属性。 - 示例:
1
type T = Exclude<1 | 2, 1 | 3> // -> 2
Extract
1
2
3
4
5/**
* 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
中共有的属性提取出来。 - 示例:
1
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
1
2
3
4
5/**
* 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
可以避免这些问题,老版本TypeScript未内置,TypeScript 3.5已经内置。 - 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22interface 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
1
2
3
4
5/**
* Exclude null and undefined from T
* 从T中排除null和undefined
*/
type NonNullable<T> = T extends null | undefined ? never : T; - 这个类型可以用来过滤类型中的 null 及 undefined 类型。
- 示例:
1
2type test = string | number | null;
type test1 = NonNullable<test>; // -> string | number;Parameters
1
2
3
4
5/**
* 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; - 该类型可以获得函数的参数类型组成的元组类型。
- 示例:
1
2
3
4
5function foo(x: number): Array<number> {
return [x];
}
type P = Parameters<typeof foo>; // -> [number] - 此时
P
的真实类型就是foo
的参数组成的元组类型\[number\]
。ConstructorParameters
1
2
3
4
5/**
* 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; - 该类型的作用是获得类的参数类型组成的元组类型。
- 示例:
1
2
3
4
5
6
7
8
9
10
11class 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
1
2
3
4
5/**
* 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
拿到函数的返回类型 - 示例:
1
2
3
4
5function foo(x: number): Array<number> {
return [x];
}
type fn = ReturnType<typeof foo>; // -> number[]InstanceType
1
2
3
4
5
6/**
* 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; - 该类型的作用是获取构造函数类型的实例类型。
1
2
3
4
5
6
7class ConstructorType {
x = 0;
y = 0;
}
type test1 = InstanceType<typeof ConstructorType>; // ConstructorType
type test1 = InstanceType<any>; // anyThisType
1
2
3
4
5/**
* Marker for contextual 'this' type
* 上下文“this”类型的标记
*/
interface ThisType<T> { } - 这个类型是用于指定上下文对象类型的。这类型怎么用呢,举个例子:这样的话,就可以指定
1
2
3
4
5
6
7
8
9interface Cat {
name: string;
age: number;
}
const obj: ThisType<Person> = {
mimi() {
this.name // string
}
}obj
里的所有方法里的上下文对象改成Person
这个类型了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 没有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中,其调用者的类型是什么。
- 本文标题:Typescript泛型帮助类型
- 本文作者:Jacshuo
- 创建时间:2021-09-20 10:15:25
- 本文链接:https://blog.imade.life/2021/09/20/Typescript泛型帮助类型/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!