TypeScript Utility Types 学习笔记及源码解析

最近写类型定义时,发现想要实现的类型不知道该用什么方法实现。也发现以前的代码里写了一些不优雅的类型定义。看了Utility以后以为仍是本身知道的太少了,想要用的这里都有了。而后去看了一下他们的源码实现,发现也不难。这里分享一下本身的学习笔记。数组

Partial<T>

将T中全部属性转换为可选属性。返回的类型能够是T的任意子集。bash

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

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

const todo: Todo = {
  title: 'First Todo',
  description: 'this is the first todo',
  done: false
};

updateTodo(todo, { done: true });
复制代码

源码:函数

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

解析: keyof T, 索引类型查询操做符。 对于任何类型 T, keyof T 的结果为 T 上已知的公共属性名的联合。学习

type todoKeys = keyof Todo; // "title" | "description" | "done"
复制代码

keyof 取到 T 中的属性名,in 操做符遍历属性名。可选属性操做符 ? 将全部属性定义为可选属性。ui

Required<T>

经过将T的全部属性设置为必选属性来构造一个新的类型。与Partial相对。this

interface Example {
  a?: string;
  b?: string;
}

const example1: Example = { a: 'aaa' }; // right

const example2: Required<Example> = { a: 'aaa' };
// error: Property 'b' is missing in type '{ a: string; }' but required in type 'Required<Example>'
复制代码

源码:spa

type Required<T> = { [P in keyof T]-?: T[P]; };
// 与partial类似,遍历T中属性,并将全部属性置为必选属性
复制代码

Readonly<T>

将T中全部属性设置为只读。翻译

interface Todo {
  title: string;
}

const todo: Readonly<Todo> = { title: 'First Todo' };

todo.title = 'New Title'; // Cannot assign to 'title' because it is a read-only property.
复制代码

源码:code

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

Record<K,T>

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

type TodoProperty = 'title' | 'description';

type Todo = Record<TodoProperty, string>;

const todo: Todo = {
  title: 'First Todo',
  description: 'this is the first todo'
};
复制代码

源码:

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

Pick<T,K>

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

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

type TodoBase = Pick<Todo, 'title' | 'done'>;

const todo: TodoBase = {
  title: 'First Todo',
  done: false
};
复制代码

源码:

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;
复制代码

Extract<T,U>

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

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

// 多参数类型约束
interface StringItem {
  type: 'stringItem'
  value: string;
}

interface NumberItem {
  type: 'numberItem'
  value: number;
}

type Item = StringItem | NumberItem;

function addCase<IType extends Item['type']>(type: IType, value: Extract<Item, { type: IType }>['value']): void {
  console.log(type, ':', value);
}

addCase('stringItem', 'value1'); // right
addCase('numberItem', 2); // right
addCase('stringItem', 1); // error:类型“1”的参数不能赋给类型“string”的参数
addCase('numberItem', 'value2'); // error:类型“"value2"”的参数不能赋给类型“number”的参数
复制代码

源码:

type Extract<T, U> = T extends U ? T : never;
复制代码

Omit<T,K>

从T中取出除去K的其余全部属性。与Pick相对。

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

type TodoBase = Omit<Todo, 'description'>;

const todo: TodoBase = {
  title: 'First Todo',
  done: false
};
复制代码

源码:

// 结合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) => 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;
复制代码

解析: 条件类型中的类型推断(高级类型章节)(原文档截图):

翻译:

在条件类型中使用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: 类型“string”不知足约束“(...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;
复制代码
相关文章
相关标签/搜索