# 定义

Typescript 提供了几种 实用工具类型, 方便进行常见的类型转换。这些实用工具类型在全局范围内都可以使用。

# 实用工具类型

变量 K, T, V 详见常见的一些泛型变量含义

# Partial<T>

Typescript 2.1 新增工具方法 (opens new window)

# 定义

构造一个类型 T, 将类型 T的所有属性设置为 可选属性。该工具类方法将返回一个表示输入类型 (T) 的所有自己的类型。

# 使用

interface Todo {
	title: string;
	description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
	return { ...todo, ...fieldsToUpdate };
}

const todo1 = {
	title: 'organize desk',
	description: 'clear clutter',
};

const todo2 = updateTodo(todo1, {
	description: 'throw out trash',
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 源码实现

type Partial<T> = {
	[P in keyof T]?: T[P];
}

1
2
3
4

直接体验 (opens new window)

# Readonly<T>

Typescript 2.1 新增工具方法 (opens new window)

# 定义

构造一个所有属性都设置为 只读 属性的类型 T, 这意味着 无法 再次对所有构造类型的属性进行 赋值

# 使用

interface Properties {
	cute: boolean;
	handsome: boolean;
}

let mine: Readonly<Properties> = {
	cute: true,
	handsome: true,
}

mine.handsome = false;
// Cannot assign to 'handsome' because it is a read-only property.(2540)

1
2
3
4
5
6
7
8
9
10
11
12
13

该工具对于表示在运行时会失败的赋值表达式很有用, 例如 Object freeze (opens new window)

function freeze<T>(obj: T): Readonly<T>;

1
2

# 源码实现

type Readonly<T> = {
	readonly [P in keyof T]: T[P];
}
1
2
3

直接体验 (opens new window)

# Record<K, T>

Typescript 2.1 新增工具方法 (opens new window)

# 定义

构造一个属性为 K 类型, 属性值为 T 类型的类型。可用于将一个类型的属性 映射到 另一个类型中。

# 使用

// Record
interface Page {
	title?: string | undefined;
	content?: string | undefined;
}

type PageType = 'home' | 'about' | 'contact';

const site: Record<PageType, Page> = {
	home: {
		content: 'home'
	},
	about: {
		title: 'about',
		content: 'about'
	},
	contact: {
		title: 'contact',
		// Error: Type '{ title: string; name: string; }' is not assignable to type 'Page'.
		// Object literal may only specify known properties,
		// and 'name' does not exist in type 'Page'.
		name: 'Rain120'
	},
}

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

# 源码实现

type Record<K extends keyof any, T> = {
	[P in K]: T;
}
1
2
3

直接体验 (opens new window)

# Pick<T, K>

Typescript 2.1 新增工具方法 (opens new window)

# 定义

通过从类型 T 选取 属性 K 的集合来构造类型。

# 使用

interface Todo {
  title: string;
  description: string;
  isCompleted: boolean;
}

type TodoWithDay = Pick<Todo, 'title' | 'isCompleted'>

const plan: TodoWithDay = {
  title: 'Writing for Typescript Guide',
  isCompleted: true
}

1
2
3
4
5
6
7
8
9
10
11
12
13

# 源码实现

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
}
1
2
3

直接体验 (opens new window)

# Omit<T, K>

Typescript 3.5 新增工具方法 (opens new window)

# 定义

通过从 类型 T选取 所有属性, 然后 删除 传入的属性 K 来构造新类型。

# 使用

interface Todo {
  title: string;
  description: string;
  isCompleted: boolean;
}

type TodoWithDay = Omit<Todo, 'description'>

const plan: TodoWithDay = {
  title: 'Writing for Typescript Guide',
  isCompleted: true
}
1
2
3
4
5
6
7
8
9
10
11
12

# 源码实现

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
1

直接体验 (opens new window)

# Exclude<T, U>

Typescript 2.8 新增工具方法 (opens new window)

# 定义

通过从类型 T剔除 可赋值给 U 的属性来构造一个新类型。

# 使用

type T0 = Exclude<"a" | "b" | "c", "a">;  // "b" | "c"

type T1 = Exclude<"a" | "b" | "c", "a" | "b">;  // "c"

type T2 = Exclude<string | number | (() => void), Function>;  // string | number

1
2
3
4
5
6

# 源码实现

type Exclude<T, U> = T extends U ? never : T;
1

直接体验 (opens new window)

# Extract<T, U>

Typescript 2.8 新增工具方法 (opens new window)

# 定义

通过从类型 T挑选 可赋值给 U 的属性来构造一个新类型。

# 使用

type T0 = Extract<'a' | 'b', 'a'> // 'a'

type T1 = Extract<'a' | 'b' | (() => void), Function> // () => void
1
2
3

# 源码实现

type Extract<T, U> = T extends U ? T : never;
1

直接体验 (opens new window)

# NonNullable<T>

Typescript 2.8 新增工具方法 (opens new window)

# 定义

# 使用

// NonNullable

type T0 = NonNullable<'a' | null> // 'a'

type T1 = NonNullable<'a' | null | undefined> // 'a'
1
2
3
4
5

# 源码实现

type NonNullable<T> = T extends null | undefined ? never : T;
1

直接体验 (opens new window)

# Parameters<T>

# 定义

构造一个关于函数类型 T参数类型 的元组类型。请到 #26019 (opens new window)

# 使用

declare function f1(arg: { a: number, b: string }): void

type T0 = Parameters<() => string>;  // []

type T1 = Parameters<(s: string) => void>;  // [string]

type T2 = Parameters<(<T>(arg: T) => T)>;  // [unknown]

type T4 = Parameters<typeof f1>;  // [{ a: number, b: string }]

type T5 = Parameters<any>;  // unknown[]

type T6 = Parameters<never>;  // never

type T7 = Parameters<string>;  // Error

type T8 = Parameters<Function>;  // Error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 源码实现

type Parameters<T> = {
  T extends (...args: infer P) => any ? P : never
}
1
2
3

直接体验 (opens new window)

# ConstructorParameters<T>

# 定义

通过 ConstructorParameters 类型, 我们可以 提取 构造函数类型的 所有参数类型。 它会生成构造函数所具有的所有参数类型的元组类型(如果 T 不是函数, 则不返回)。

# 使用

type T0 = ConstructorParameters<ErrorConstructor>;  // [(string | undefined)?]

type T1 = ConstructorParameters<FunctionConstructor>;  // string[]

type T2 = ConstructorParameters<RegExpConstructor>;  // [string, (string | undefined)?]
1
2
3
4
5

# 源码实现

type ConstructorParameters<T> = {
  T extends new (...args: infer P) => any ? P : never
}
1
2
3

直接体验 (opens new window)

# ReturnType<T>

Typescript 2.8 新增工具方法 (opens new window)

# 定义

构造一个由函数 T 的返回类型组成的新类型。

# 使用

declare function f1(): { a: number, b: string }

type T0 = ReturnType<() => string>;  // string

type T1 = ReturnType<(s: string) => void>;  // void

type T2 = ReturnType<(<T>() => T)>;  // {}

type T3 = ReturnType<(<T extends U, U extends number[]>() => T)>;  // number[]

type T4 = ReturnType<typeof f1>;  // { a: number, b: string }

type T5 = ReturnType<any>;  // any

type T6 = ReturnType<never>;  // any

type T7 = ReturnType<string>;  // Error

type T8 = ReturnType<Function>;  // Error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 源码实现

type ReturnType<
  T extends (...args: any[]) => any
> = T extends (...ages: any[]) => infer R ? R : any;
1
2
3

直接体验 (opens new window)

# InstanceType<T>

Typescript 2.8 新增工具方法 (opens new window)

# 定义

构造y一个由构造函数 T实例类型 组成的新类型。

# 使用

class C {
	x = 0;
	y = 0;
}


type T0 = InstanceType<typeof C>;  // C

type T1 = InstanceType<any>;  // any

type T2 = InstanceType<never>;  // any

type T3 = InstanceType<string>;  // Error

type T4 = InstanceType<Function>;  // Error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 源码实现

type InstanceType<
  T extends new (...args: any[]) => any
> = T extends new (...args: any[]) => infer R : R : any;
1
2
3

直接体验 (opens new window)

# Required<T>

Typescript 2.8 rc 新增工具方法 (opens new window)

# 定义

# 使用

interface Profile {
  name: string;
  age?: number;
  gender?: string;
};

// OK
const profile: Profile = {
  name: 'Rain120',
};

// Type '{ name: string; }' is missing the following properties
// from type 'Required<Profile>': age, gender(2739)
const ID_Card: Required<Profile> = { 
  name: 'Rain120',
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 源码实现

type Required<T> = {
  [P in keyof T]-?: T[P];
}
1
2
3

操作符相关问题请到 Here

直接体验 (opens new window)

# ThisParameterType

# 定义

提取函数类型的 this 参数的类型, 如果函数类型没有 this 参数, 则 未知 (opens new window)

注意: 仅当启用 --strictFunctionTypes 时, 此类型才能正常工作。 请到 #32964 (opens new window)

# 使用

function toHex(this: Number) {
  return this.toString(16);
}

function numberToString(n: ThisParameterType<typeof toHex>) {
  return toHex.apply(n);
}
1
2
3
4
5
6
7

直接体验 (opens new window)

# OmitThisParameter

# 定义

从函数类型中删除 this 参数。

注意: 仅当启用 --strictFunctionTypes 时, 此类型才能正常工作。请到 #32964 (opens new window)

# 使用

function toHex(this: Number) {
	return this.toString(16);
}

// The return type of `bind` is already using `OmitThisParameter`, this is just for demonstration.
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);

console.log(fiveToHex());
1
2
3
4
5
6
7
8

直接体验 (opens new window)

# ThisType<T>

# 定义

该工具 不会返回转换后 的类型。 相反, 它是一个用作上下文类型中键入 this 类型的标记。 注意, 必须启用 –noImplicitThis 标志才能使用此工具。

# 使用

// Compile with --noImplicitThis

type ObjectDescriptor<D, M> = {
  data?: D;
  methods?: M & ThisType<D & M>;  // Type of 'this' in methods is D & M
}

function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
  let data: object = desc.data || {};
  let methods: object = desc.methods || {};
  return { ...data, ...methods } as D & M;
}

let obj = makeObject({
  data: { x: 0, y: 0 },
  methods: {
    moveBy(dx: number, dy: number) {
      this.x += dx;  // Strongly typed this
      this.y += dy;  // Strongly typed this
    }
  }
});

obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);

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
  • 在上面的示例中, makeObject 的参数中的 methods 对象具有包含 ThisType 的上下文类型; 因此, 在 methods 对象内的方法中, 方法的 this 类型为 {x: number, y: number}&{moveBy (dx: number, dy: number) : number}。 请注意 methods 属性的类型是如何同时成为方法中 this 类型的推断目标和来源的。
  • ThisType 标记接口仅仅是在 lib.d.ts 中声明的一个空接口。 除了在对象字面量的上下文类型中被识别外, 该接口的作用类似于任何空接口。

直接体验 (opens new window)

# 快来耍耍啊

# 🌰🌰

// template
1

# 游乐场


# 参考答案

// answer
1

# 参考资料

handbook - utility-types (opens new window)

handbook - mapped-types (opens new window)

Microsoft TypeScript wiki Road map (opens new window)

Microsoft TypeScript #21316 Conditional Types (opens new window)

Microsoft TypeScript #21496 infer pull request (opens new window)

Conditional types in TypeScript (opens new window)