术语解释:
extends:条件类型,和class中的继承是两码事。keyof:与Object.keys相似,keyof取interface或type的键,是索引类型操作符。in:检测属性是否在对象中。infer:表示在extends条件语句中待推断的类型变量,用于「替换手动获取类型」。&:交叉类型。|:联合类型。?:可选属性。-?:去掉可选,变成必选属性。readonly:只读属性。
# Partial
Partial<T>将类型T的所有属性标记为可选属性。
type User = {
name: string;
age: number;
}
type PartialUser = Partial<User>
// 等效于
type PartialUser2 = {
name?: string;
age?: number;
}
// 源码实现
type Partial<T> = { [P in keyof T]?: T[P]; }
// keyof 取到 T 中的属性名,in 操作符遍历属性名。
// 可选属性操作符 ? 将属性定义为可选属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Required
Required<T>将类型T的所有属性标记为必选属性。
type User = {
name?: string;
age?: number;
}
type RequiredUser = Required<User>
// 等效于
type RequiredUser2 = {
name: string;
age: number;
}
// 源码实现
type Required<T> = { [P in keyof T]-?: T[P]; }
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# Readonly
Readonly<T>将类型T的所有属性标记为readonly属性,即不能修改。
type User = {
name: string;
age?: number;
}
type ReadonlyUser = Readonly<User>
// 等效于
type ReadonlyUser2 = {
readonly name: string;
readonly age?: number;
}
const user: ReadonlyUser = { name: '123', age: 21 }
user.name = 'xxx' // 无法分配到 "name" ,因为它是只读属性
// 源码实现
type Readonly<T> = { readonly [P in keyof T]: T[P]; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Record
Record<K, T>构造一个类型,该类型具有一组属性K,每个属性的类型为T。可用于将一个类型的属性映射为另一个类型。
type keys = 'name' | 'age'
type User = Record<keys, string>
// 等效于
type User1 = {
name: string;
age: string;
}
// 源码实现
type ToRecord<K extends keyof any, T> = {
[P in K]: T;
};
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# Pick
Pick<T, K>在T中抽取一组属性K构建一个新类型。
type Todo = {
title: string;
description: string;
completed: boolean;
}
type preview = Pick<Todo, 'title' | 'description'>
// 等效于
type preview1 = {
title: string;
description: string;
}
// 源码实现
type ToPick<T, K extends keyof T> = {
[P in K]: T[P];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Omit
Omit<T, K>从T中去除一组属性K构建一个新类型。
type Todo = {
title: string;
description: string;
completed: boolean;
}
type preview = Omit<Todo, 'completed' | 'description'>
// 等效于
type preview1 = {
title: string;
}
// 源码实现 结合Exclude
type Omit<T, K extends keyof any> = {
[P in Exclude<keyof T, K>]: T[P]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Extract
Extract<T, U>提取T中可以赋值给U的类型(即提取T与U的交集)。
type T0 = Extract<"a" | "b" | "c", "a" | "f">;
// type T0 = "a"
type T1 = Extract<string | number | (() => void), Function>;
// type T1 = () => void
// 源码实现
type Extract<T, U> = T extends U ? T : never;
1
2
3
4
5
6
7
2
3
4
5
6
7
# Exclude
Exclude<T, U>从T中剔除可以赋值给U的类型(返回T中除了U的类型)。
type T0 = Exclude<"a" | "b" | "c", "a">;
// type T0 = "b" | "c"; // 去掉a
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
// type T1 = "c"; // 去掉a,b
type T2 = Exclude<string | number | (() => void), Function>;
// type T2 = string | number; // 去掉function
// 源码实现
type Exclude<T, U> = T extends U ? never : T;
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
TIPS
Pick<T, K>和Omit<T, K>为一组,都是对含键值的类型或接口进行操作,参数T必须是有键值对的,参数K是联合类型。Extract<T, U>和Exclude<T, U>为一组,都是对联合类型就行操作,两个参数都为联合类型。
# NonNullable
NonNullable<T>从T中剔除null和undefined。
type T0 = NonNullable<string | number | undefined>;
// type T0 = string | number
type T1 = NonNullable<string[] | null | undefined>;
// type T1 = string[]
// 源码实现 只用继承自null或者undefined就行
type NonNullable<T> = T extends null | undefined ? never : T; // 4.8版本之前
type NonNullable<T> = T & {}; // 4.8版本之后
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# Parameters
Parameters<T>返回类型为T的函数的参数类型所组成的元组。
type T0 = Parameters<() => string>;
// type T0 = []
type T1 = Parameters<(s: string) => void>;
// type T1 = [s: string]
function f1(arg: { a: number; b: string }): void {}
type T2 = Parameters<typeof f1>;
// type T2 = [arg: {
// a: number;
// b: string;
// }]
// 源码实现
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# ConstructorParameters
ConstructorParameters<T>返回T构造函数的参数类型所组成的元组,如果T不是函数则为nerver。
interface Person {
new(name: string, age: number): void;
name: string;
age: number;
}
type T0 = ConstructorParameters<Person>;
// type T0 = [name: string, age: number]
// 源码实现
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# ReturnType
ReturnType<T>获取函数类型T的返回值类型。
type T0 = ReturnType<() => string>;
// type T0 = string
type T1 = ReturnType<(s: string) => void>;
// type T1 = void
declare function f1(): { a: number; b: string };
type T2 = ReturnType<typeof f1>;
// type T2 = {
// a: number;
// b: string;
// }
// 源码实现
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# InstanceType
InstanceType<T>获取构造函数类型的实例类型。
class C {
x = 0;
y = 0;
}
type T0 = InstanceType<typeof C>;
// type T0 = C
// 源码实现
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# ThisParameterType
ThisParameterType<T>只提取函数类型T中的this参数类型,如果没有this参数则为unknown。
function test(this: string[], name: string): string[] {
this.push(name);
return this;
}
type T0 = ThisParameterType<typeof test>
// type T0 = string[]
// 源码实现
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# OmitThisParameter
OmitThisParameter<T>删除函数类型T中的this参数,如果没有显示定义this这个参数,那么就是一个简单的类型。
function toHex(this: Number) {
return this.toString(16);
}
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);
// fiveToHex: () => string
// 源码实现
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# ThisType
ThisType<T>可以在对象字面量中键入this,并提供通过上下文类型控制this类型的便捷方式。它只有在--noImplicitThis的选项下才有效。
现在,在对象字面量方法中的this类型,将由以下决定:
- 如果这个方法显式指定了
this参数,那么this具有该参数的类型。(下例子中bar) - 否则,如果方法由带
this参数的签名进行上下文键入,那么this具有该参数的类型。(下例子中foo) - 否则,如果
--noImplicitThis选项已经启用,并且对象字面量中包含由ThisType<T>键入的上下文类型,那么this的类型为T。 - 否则,如果
--noImplicitThis选项已经启用,并且对象字面量中不包含由ThisType<T>键入的上下文类型,那么this的类型为该上下文类型。 - 否则,如果
--noImplicitThis选项已经启用,this具有该对象字面量的类型。 - 否则,
this的类型为any。
// Compile with --noImplicitThis
type Point = {
x: number;
y: number;
moveBy(dx: number, dy: number): void;
};
let p: Point = {
x: 10,
y: 20,
moveBy(dx, dy) {
this.x += dx; // this has type Point
this.y += dy; // this has type Point
}
};
let foo = {
x: 'hello',
f(n: number) {
this; // { x: string, f(n: number): void }
}
};
let bar = {
x: 'hello',
f(this: { message: string }) {
this; // { message: string }
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 内在字符串操作类型
# Uppercase
Uppercase<StringType>将字符串中的每个字符转换为大写版本。
type Greeting = "Hello, world"
type ShoutyGreeting = Uppercase<Greeting>
// type ShoutyGreeting = "HELLO, WORLD"
1
2
3
2
3
# Lowercase
Lowercase<StringType>将字符串中的每个字符转换为小写版本。
type Greeting = "Hello, world"
type QuietGreeting = Lowercase<Greeting>
// type QuietGreeting = "hello, world"
1
2
3
2
3
# Capitalize
Capitalize<StringType>首字母大写,其他不变。
type LowercaseGreeting = "hello, world";
type Greeting = Capitalize<LowercaseGreeting>;
// type Greeting = "Hello, world"
1
2
3
2
3
# Uncapitalize
Uncapitalize<StringType>首字母小写,其他不变。
type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;
// type UncomfortableGreeting = "hELLO WORLD"
1
2
3
2
3