内置工具类型

在前面的章节中,我们已经陆续介绍了一些 TypeScript 内置的实用工具类型。这些工具类型的定义位于 TypeScript 语言安装目录下的 lib/lib.es5.d.ts 文件中。这里推荐读者去阅读这部分源代码,以便能够更好地理解工具类型,同时也能学习到一些类型定义的技巧。在 TypeScript 程序中,不需要进行额外的安装或配置就可以直接使用这些工具类型。

目前,TypeScript 提供的所有内置工具类型如下:

  • Partial<T>

  • Required<T>

  • Readonly<T>

  • Record<K, T>

  • Pick<T, K>

  • Omit<T, K>

  • Exclude<T, U>

  • Extract<T, U>

  • NonNullable<T>

  • Parameters<T>

  • ConstructorParameters<T>

  • ReturnType<T>

  • InstanceType<T>

  • ThisParameterType<T>

  • OmitThisParameter<T>

  • ThisType<T>

接下来,我们将分别介绍这些工具类型。

Partial<T>

该工具类型能够构造一个新类型,并将实际类型参数 T 中的所有属性变为可选属性。示例如下:

interface A {
    x: number;
    y: number;
}

type T = Partial<A>; // { x?: number; y?: number; }

const a: T = { x: 0, y: 0 };
const b: T = { x: 0 };
const c: T = { y: 0 };
const d: T = {};

Required<T>

该工具类型能够构造一个新类型,并将实际类型参数 T 中的所有属性变为必选属性。示例如下:

interface A {
    x?: number;
    y: number;
}

type T0 = Required<A>; // { x: number; y: number; }

Readonly<T>

该工具类型能够构造一个新类型,并将实际类型参数 T 中的所有属性变为只读属性。示例如下:

interface A {
    x: number;
    y: number;
}

// { readonly x: number; readonly y: number; }
type T = Readonly<A>;

const a: T = { x: 0, y: 0 };
a.x = 1;   // 编译错误!不允许修改
a.y = 1;   // 编译错误!不允许修改

Record<K,T>

该工具类型能够使用给定的对象属性名类型和对象属性类型创建一个新的对象类型。Record<K, T> 工具类型中的类型参数 K 提供了对象属性名联合类型,类型参数 T 提供了对象属性的类型。示例如下:

type K = 'x' | 'y';
type T = number;
type R = Record<K, T>; // { x: number; y: number; }

const a: R = { x: 0, y: 0 };

因为类型参数 K 是用作对象属性名类型的,所以实际类型参数 K 必须能够赋值给 string | number | symbol 类型,只有这些类型能够作为对象属性名类型。

Pick<T,K>

该工具类型能够从已有对象类型中选取给定的属性及其类型,然后构建出一个新的对象类型。Pick<T, K> 工具类型中的类型参数 T 表示源对象类型,类型参数 K 提供了待选取的属性名类型,它必须为对象类型 T 中存在的属性。示例如下:

interface A {
    x: number;
    y: number;
}

type T0 = Pick<A, 'x'>;        // { x: number }
type T1 = Pick<A, 'y'>;        // { y: number }
type T2 = Pick<A, 'x' | 'y'>;  // { x: number; y: number }

type T3 = Pick<A, 'z'>;
//                ~~~
//                编译错误:类型'A'中不存在属性'z'

Omit<T,K>

Omit<T, K> 工具类型与 Pick<T, K> 工具类型是互补的,它能够从已有对象类型中剔除给定的属性,然后构建出一个新的对象类型。Omit<T, K> 工具类型中的类型参数 T 表示源对象类型,类型参数 K 提供了待剔除的属性名类型,但它可以为对象类型 T 中不存在的属性。示例如下:

interface A {
    x: number;
    y: number;
}

type T0 = Omit<A, 'x'>;       // { y: number }
type T1 = Omit<A, 'y'>;       // { x: number }
type T2 = Omit<A, 'x' | 'y'>; // { }
type T3 = Omit<A, 'z'>;       // { x: number; y: number }

Exclude<T,U>

该工具类型能够从类型 T 中剔除所有可以赋值给类型 U 的类型。示例如下:

type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | (() => void), Function>; // string

Extract<T,U>

Extract<T, U> 工具类型与 Exclude<T, U> 工具类型是互补的,它能够从类型 T 中获取所有可以赋值给类型 U 的类型。示例如下:

type T0 = Extract<'a' | 'b' | 'c', 'a' | 'f'>; // 'a'
type T1 = Extract<string | (() => void), Function>; // () => void
type T2 = Extract<string | number, boolean>;        // never

NonNullable<T>

该工具类型能够从类型 T 中剔除 null 类型和 undefined 类型并构造一个新类型,也就是获取类型 T 中的非空类型。示例如下:

// string | number
type T0 = NonNullable<string | number | undefined>;

// string[]
type T1 = NonNullable<string[] | null | undefined>;

Parameters<T>

该工具类型能够获取函数类型 T 的参数类型并使用参数类型构造一个元组类型。示例如下:

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

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

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

type T4 = Parameters<
    (x: { a: number; b: string }) => void
>;                                         // [{ a: number, b: string }]

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

type T6 = Parameters<never>;               // never

type T7 = Parameters<string>;
//                   ~~~~~~~
//                   编译错误!string类型不符合约束'(...args: any) => any'

type T8 = Parameters<Function>;
//                   ~~~~~~~~
//                   编译错误!Function类型不符合约束'(...args: any) => any'

ConstructorParameters<T>

该工具类型能够获取构造函数 T 中的参数类型,并使用参数类型构造一个元组类型。若类型 T 不是函数类型,则返回 never 类型。示例如下:

// [string, number]
type T0 = ConstructorParameters<new (x: string, y: number) => object>;

// [(string | undefined)?]
type T1 = ConstructorParameters<new (x?: string) => object>;

type T2 = ConstructorParameters<string>;   // 编译错误
type T3 = ConstructorParameters<Function>; // 编译错误

ReturnType<T>

该工具类型能够获取函数类型 T 的返回值类型。示例如下:

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

// { a: string; b: number }
type T1 = ReturnType<() => { a: string; b: number }>;

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

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

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

// any
type T5 = ReturnType<never>;

type T6 = ReturnType<boolean>;   // 编译错误
type T7 = ReturnType<Function>;  // 编译错误

InstanceType<T>

该工具类型能够获取构造函数的返回值类型,即实例类型。示例如下:

class C {
    x = 0;
}
type T0 = InstanceType<typeof C>;         // C

type T1 = InstanceType<new () => object>; // object

type T2 = InstanceType<any>;              // any

type T3 = InstanceType<never>;            // any

type T4 = InstanceType<string>;           // 编译错误
type T5 = InstanceType<Function>;         // 编译错误

ThisParameterType<T>

该工具类型能够获取函数类型 Tthis 参数的类型,若函数类型中没有定义 this 参数,则返回 unknown 类型。在使用 ThisParameterType<T> 工具类型时需要启用 --strict-FunctionTypes 编译选项。示例如下:

/**
 * --strictFunctionTypes=true
 */

function f0(this: object, x: number) {}
function f1(x: number) {}

type T0 = ThisParameterType<typeof f0>;  // object
type T1 = ThisParameterType<typeof f1>;  // unknown
type T2 = ThisParameterType<string>;     // unknown

OmitThisParameter<T>

该工具类型能够从类型 T 中剔除 this 参数类型,并构造一个新类型。在使用 OmitThisParameter<T> 工具类型时需要启用 --strictFunctionTypes 编译选项。示例如下:

/**
 * --strictFunctionTypes=true
 */

function f0(this: object, x: number) {}
function f1(x: number) {}

// (x: number) => void
type T0 = OmitThisParameter<typeof f0>;

// (x: number) => void
type T1 = OmitThisParameter<typeof f1>;

// string
type T2 = OmitThisParameter<string>;

ThisType<T>

该工具类型比较特殊,它不是用于构造一个新类型,而是用于定义对象字面量的方法中 this 的类型。如果对象字面量的类型是 ThisType<T> 类型或包含 ThisType<T> 类型的交叉类型,那么在对象字面量的方法中 this 的类型为 T。在使用 ThisType<T> 工具类型时需要启用 --noImplicitThis 编译选项。示例如下:

/**
 * --noImplicitThis=true
 */

let obj: ThisType<{ x: number }> & { getX: () => number };

obj = {
    getX() {
        this; // { x: number; y: number; }

        return this.x;
    },
};

此例中,使用交叉类型为对象字面量 obj 指定了 ThisType<T> 类型,因此 objgetX 方法的 this 类型为 { x: number; } 类型。