TypeScript Utility Types 源码阅读

本文是对《TypeScript Utility Types 学习笔记及源码解析》的重新编写和排版,首发于微信公众号“依赖注入”。

Partial<T>

将 T 中所有属性转换为可选属性,返回的类型可以是 T 的任意子集。这在需要支持接受部分属性的场景下非常有用。

interface IPerson {
  name: string;
  age: number;
}

function patchPersonInfo(person: Partial<IPerson>) {
  return $http.patch('/persion/id', person);
}

patchPersonInfo({
  name: 'new_name'
});

源码:

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

对于任何类型 T, keyof T 的结果为 T 上已知的公共属性名的联合。

Required<T>

Partial 相对,通过将 T 的所有属性设置为必选属性来构造一个新的类型,这在需要收敛类型的场景下非常有用。

interface IPerson {
  name?: string;
  age?: number;
}

function patchPersonInfo(person: Required<IPerson>) {
  return $http.put('/persion/id', person);
}

patchPersonInfo({
  name: 'new_name'
});

// error: Property 'age' is missing in type '{ name: string; }' but required in type 'Required<IPerson>'

源码:

type Required<T> = {
 [P in keyof T]-?: T[P];
};
// 与 `Partial` 相似,遍历T中属性,并将所有属性置为必选属性

Readonly<T>

将 T 中所有属性设置为只读。

interface IPerson {
  name: string;
  age: number;
}

let person: Readonly<IPerson> = {
  name: 'my_name',
  age: 18
};

person.age = 19;
// error: Cannot assign to 'age' because it is a read-only property

源码:

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

Record<K, T>

构造一个类型,该类型具有一组属性 K,每个属性的类型为 T。可用于将一个类型的属性映射为另一个类型。

type IPersonKeys = 'name' | 'address';

type IPerson = Record<IPersonKeys, string>;

const person: IPerson = {
  name: 'my_name',
  address: 'my_address'
};

源码:

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

Pick<T, K>

通过在 T 中抽取一组属性 K 构建一个新类型。

interface IPerson {
  name:  string;
  age: number;
  address: string;
  height: number;
  weight: number;
}

type ISimplePerson = Pick<IPerson, 'name' | 'age'>;

const simplePerson: ISimplePerson = {
  name: 'First Todo',
  age: 18
};

源码:

type Pick<T, K extends keyof T> = {
 [p in K]: T[p];  // K 是 T的属性集合的子集
};

Exclude<T, U>

从 T 中排除可分配给 U 的属性,剩余的属性构成新的类型

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

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

源码:

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

Never 作为 Typescript 类型系统里的 Bottom Type 可用于删除属性。

Extract<T, U>

Exclude 相对,从 T 中抽出可分配给 U 的属性构成新的类型。

type T0 = 'a' | 'b' | 'c';
type T1 = 'a' | 'c' | 'd' | 'e' ;

type T2 = Extract<T0, T1>;  // "a" | "c"

源码:

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

Omit<T,K>

从 T 中取出除去 K 的其他所有属性,与 Pick 相对。

interface IPerson {
  name: string;
  age: number;
  address: string;
}

const person: Omit<IPerson, 'address'> = {
  name: 'my_name',
  age: 18
};

源码:

// 结合 Exclude 和 Pick 实现
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

NonNullable<T>

去除 T 中的 null 和 undefined 类型。

type T0 = NonNullable<number | string | undefined> 
// number | string
type T1 = NonNullable<number | null | undefined> 
// number

源码:

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

Parameters<T>

返回类型为 T 的函数的参数类型所组成的数组。

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: 类型“string”不满足约束“(...args: any) =>

type T8 = Parameters<Function>;  
// Error: 类型“Function”不满足约束“(...args: any) => any”。
// 类型“Function”提供的内容与签名“(...args: any): any”不匹配

源码:

type ConstructorParameters<T extends new (...args: any) => any> 
= T extends new (...args: infer P) => any ? P : never;

image

在条件类型中使用 extends 时,可以声明一个 infer 来表示有一个类型变量需要推断。这个可推断的类型变量可以在条件类型的 true 分支上引用。同一个类型变量可能有多个推断的位置。

type Foo<T> = T extends { a: infer U, b: infer U } ? U : never;

type T10 = Foo<{ a: string, b: string }>;  
// string

type T11 = Foo<{ a: string, b: number }>;  
// string | number
ReturnType<T>
function 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:类型“Function”不满足约束“(...args: any) => any”。

type T8 = ReturnType<Function>;  
// Error:类型“Function”不满足约束“(...args: any) => any”。
// 类型“Function”提供的内容与签名“(...args: any): any”不匹配

源码:

type ReturnType<T extends (...args: any) => any> 
= T extends (...args: any) => infer R ? R : any;

InstanceType<T>

返回构造函数类型 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:类型“string”不满足约束“new (...args: any) => any”

type T4 = InstanceType<Function>; 
// error:类型“Function”不满足约束“new (...args: any) => any”。
// 类型“Function”提供的内容与签名“new (...args: any): any”不匹配

源码:

type InstanceType<T extends new (...args: any) => any> 
= T extends new (...args: any) => infer R ? R : any;

参考资料: