typescript类型运算

Union Types 联合类型 “|”

A | B 表示一个类型为 A 或 B 的实体.

1
2
3
type A = string;
type B = number;
type C = A | B;

type C 的类型为字符串或者数字

1
2
3
function test(value: C) {
...
}

这个方法的入参必须是字符串或者数字


Intersection Types 交叉类型 “&” (TypeScript 1.6 加入)

A & B 表示一个类型同时为 A 和 B 的实体.

1
2
3
4
type A = { a: string };
type B = { b: number };
// C 类型为 { a: string; b: number }
type C = A & B;

索引类型 “keyof” (TypeScript 2.1 加入)

keyof T 返回一个类型,这个类型是一个 string literal 的 union,内容是 T 中所有的属性名 (key)。

例: keyof { a: 1, b: 2 } 得到的类型是 “a” | “b”


约束类型 “extends”

这里的 extends 关键词不同于在 class 后使用 extends 的继承作用,泛型内使用的主要作用是对泛型加以约束

1
2
3
4
5
6
7
8
9
10
11
type T = {
a: string;
b: string;
};
function test<K extends keyof T>(key: K) {
const obj = {
a: "1",
b: "2",
};
return obj[key];
}

上面的参数 key 只能是字符串‘a’或字符串‘b’


条件类型 (TypeScript 2.8 加入)

1
T extends U ? X : Y

上面的类型意思是,若 T 能够赋值给 U,那么类型是 X,否则为 Y。


Lookup Types 查找类型

[] 的类型版。

T[K] 返回 (类型 T 中以 K 为属性名的值) 的类型。K 必须是 keyof T 的子集,可以是一个字符串字面量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const a = { k1: 1, k2: "v2" };

// tv1 为number
type tv1 = typeof a["k1"];

// tv2 为string
type tv2 = typeof a["k2"];

// tv$ 为 (number|string): 属性名的并集对应到了属性值的类型的并集
type tv$ = typeof a["k1" | "k2"];

// 以上的括号不是必需的: typeof 优先级更高

// 也可以用于获取内置类型 (string 或 string[]) 上的方法的类型

// (pos: number) => string
type t_charAt = string["charAt"];

// (...items: string[]) => number
type t_push = string[]["push"];

Mapped Types 映射类型 “in”(TypeScript 2.1 加入)

我们可以在类型定义中引用其他类型的 (部分或全部) 属性,并对其进行运算,用运算结果定义出新的类型 (Mapped Type)。即”把旧类型的属性 map (映射) 成新类型的属性”,可以比作 list comprehension (把旧 list 的成员 map 成新 list 的成员) 的类型属性版。

引用哪些属性同样是通过一个 string literal 的 union 来定义的。这个 union 必须是 keyof 旧类型 的子集,可以是一个或多个 string literal,也可以是 keyof 的返回值 (即映射全部属性)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface A {
k1: string;
k2: string;
k3: number;
}

// 从A中取一部分属性,类型不变 (A[P] 是上面讲的查找类型)
// 结果: type A_var1 = { k1: string, k3: number }
type A_var1 = {
[P in "k1" | "k3"]: A[P];
}

// 从A中取所有属性, 类型改为number
// 结果: type A_var1 = { k1: number, k2: number, k3: number }
// **注意** keyof / Mapped type / 泛型一起使用时有一些特殊规则。建议读一下最后一部分 "DeepReadonly 是怎样展开的"
type A_var2 = {
[P in keyof A]: number;
}

// 从A中取所有属性, 类型改为相应的Promise (TS 2.1 release note中的Deferred是这个的泛型版)
type A_var3 = {
[P in keyof A]: Promise<A[P]>;
}

工具泛型

TypesScript 中内置了很多工具泛型,内置的泛型在 TypeScript 内置的 lib.es5.d.ts 中都有定义,所以不需要任何依赖都是可以直接使用的,直达链接

Partial

Partial 用于将一个接口的所有属性设置为可选状态,首先通过 keyof T ,取出类型变量 T 的所有属性,然后通过 in 进行遍历,最后在属性后加上一个 ?
上定义:

1
2
3
4
// 官方定义
type Partial<T> = {
[P in keyof T]?: T[P];
};
Required

Required 的作用刚好与 Partial 相反,就是将接口中所有可选的属性改为必须的,区别就是把 Partial 里面的 ? 替换成了 -?

1
2
3
4
// 官方定义
type Required<T> = {
[P in keyof T]-?: T[P];
};
Readonly

将一个接口的所有属性设置为只读状态

1
2
3
4
// 官方定义
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
Pick

从一个复合类型中,取出几个想要的类型的组合

1
2
3
4
// 官方定义
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
// 原始类型
interface TState {
name: string;
age: number;
like: string[];
}
// 如果我只想要name和age怎么办,最粗暴的就是直接再定义一个(我之前就是这么搞得)
interface TSingleState {
name: string;
age: number;
}
// 这样的弊端是什么?就是在Tstate发生改变的时候,TSingleState并不会跟着一起改变,所以应该这么写
interface TSingleState extends Pick<TState, "name" | "age"> {}
Record

将一个类型的所有属性值都映射到另一个类型上并创造一个新的类型

1
2
3
4
// 官方定义
type Record<K extends keyof any, T> = {
[P in K]: T;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type petsGroup = 'dog' | 'cat' | 'fish';
interface IPetInfo {
name:string,
age:number,
}

type IPets = Record<petsGroup, IPetInfo>;

const animalsInfo:IPets = {
dog:{
name:'dogName',
age:2
},
cat:{
name:'catName',
age:3
},
fish:{
name:'fishName',
age:5
}
}
Exclude

Exclude<T, U>的意思是从 T 中排除那些可分配给 U 的类型

1
2
// 官方定义
type Exclude<T, U> = T extends U ? never : T;
1
2
3
4
5
6
7
8
9
// A 类型为10
type A = Exclude<"a" | 10, "a" | "b" | "c">;

// 你以为的 Exclude
type A = "a" | 10 extends "a" | "b" | "c" ? never : "a" | 10;
// 实际上的 Exclude
type A =
| ("a" extends "a" | "b" | "c" ? never : "a")
| (10 extends "a" | "b" | "c" ? never : 10);
Extract

Extract<T, U>的意思是从 T 中提取可分配给 U 的类型

1
2
// 官方定义
type Extract<T, U> = T extends U ? T : never;

和 Exclude 相反

1
2
3
4
// number
type A = Exclude<number | string, string>;
// string
type B = Extract<number | string, string>;
Omit

Omit<T, U>的意思是从对象类型 T 中剔除 U 属性并创建一个新的属性

1
2
// 官方定义
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
1
2
3
4
5
6
type User = {
id: number;
name: string;
email: string;
};
type UserWithoutEmail = Omit<User, "email">;

等价于:

1
2
3
4
type UserWithoutEmail = {
id: number;
name: string;
}
ReturnType

ReturnType<T>返回了范型参数 T 的返回值类型

1
2
// 官方定义
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
1
2
// A: string
type A = ReturnType<() => string>;
InstanceType

获取构造函数的实例类型

1
2
// 官方定义
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;