Utility Types

101 阅读6分钟

Indexed Access Types 索引访问类型

可以使用索引访问类型来查找另一个类型上的特定属性:

type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"]; 

// 上面的类型等同于
type Age = number;

索引类型本身就是一个类型,所以我们可以使用unionkeyof或其他类型:

type T1 = Person["age" | "name"];
// 等同于
type T1 = string | number

type T2 = Person[keyof Person];
// 等同于
type T2 = string | number | boolean;

type AliveOrName = "alive" | "name";
type T3 = Person[AliveName];
// 等同于
type T3 = string | boolean;

另一个使用任意类型进行索引的例子是使用number来获取数组元素的类型。可以将它与typeof结合起来,方便捕获数组字面量的元素类型:

const MyArray = [
  { name: 'Alice', age: 15 },
  { name: 'Bob', age: 23 },
  { name: 'Eve', age: 38 },
]

type Person = typeof MyArray[number]
// 等同于
// type Person = { name: string, age: number }

type Age = (typeof MyArray[number])['age']
// 等同于
type Age2 = Person['age'] // type Age2 = number

只能在索引时使用类型,这意味着你不能使用const来做变量引用:

const key = "age";
type Age = Person[key]; // 报错
// Type 'key' cannot be used as an index type.  
// key不能被用作索引类型
// 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'?
// “key”指的是一个值,但在这里被用作类型。你是说“typeof key”吗?

// 所以只需要做一点修改即可
type Age = Person[typeof key];

Awaited

该类型用于模拟async函数中的await操作,或者Promise上的.then()方法——具体来说,就是它们递归展开Promise的方式。

type A = Awaited<Promise<string>>;
//   ^?

type B = Awaited<Promise<Promise<number>>>;
//   ^?

type C = Awaited<boolean | Promise<number>>;
//   ^?

Partial

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

构造一个将type的所有属性设置为可选的类型。此实用程序将返回表示给定类型的所有子集的类型。

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

// Partial<Todo> 得到的结果是
interface PartialTodo {
    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",
});

Required

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

Partial的反义词。

interface Props {
  a?: number;
  b?: string;
}

const obj: Props = { a: 5 };

// Required<Props> 得到的结果是
interface RequiredProps {
  a: number;
  b: string;
}

const obj2: Required<Props> = { a: 5 }; // 报错 Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

Readonly

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

将每个属性设置为只读属性,后期不可以更改

interface Todo {
  title: string;
}

const todo: Readonly<Todo> = {
  title: "Delete inactive users",
};

todo.title = "Hello"; // 报错 Cannot assign to 'title' because it is a read-only property.

Record

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

构造一个对象类型,其属性的keyKeys类型,属性值为Type类型。它可以将一个类型的属性映射到另一个类型。

type CatName = "miffy" | "boris" | "mordred";

interface CatInfo {
  age: number;
  breed: string;
}

const cats: Record<CatName, CatInfo> = {
  miffy: { age: 10, breed: "Persian" },
  boris: { age: 5, breed: "Maine Coon" },
  mordred: { age: 16, breed: "British Shorthair" },
};

cats.boris; // const cats: Record<CatName, CatInfo>

Pick 对象字段挑选类型

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

Type中选出一组属性Keys(字符串字面量值或字符串字面量值的并集)来构造一个类型。

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

type TodoPreview = Pick<Todo, "title" | "completed">;

// 翻译过来就是
interface TodoPreviewPick {
    title: string;
    completed: boolean;
}

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

todo; // TodoPreview

Omit 对象字段排除类型

/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

构造一个除类型K以外具有T属性的类型。与Pick相反。

interface Todo {
  title: string;
  description: string;
  completed: boolean;
  createdAt: number;
}

type TodoPreview = Omit<Todo, "description">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
  createdAt: 1615544252770,
};

todo; // TodoPreview

type TodoInfo = Omit<Todo, "completed" | "createdAt">;

const todoInfo: TodoInfo = {
  title: "Pick up kids",
  description: "Kindergarten closes at 5pm",
};

todoInfo; // TodoInfo

Exclude 联合补集类型

/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T;

Exclude<UnionType, ExcludedMembers>,通过从UnionType中排除可分配给ExcludedMembers的所有联合成员来构造类型。

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

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; x: number }
  | { kind: "triangle"; x: number; y: number };

type T3 = Exclude<Shape, { kind: "circle" }>
//   type T3 = {  kind: "square";  x: number;  } | {  kind: "triangle";  x: number;  y: number;  }

Extract 联合相交类型

/**
 * Extract from T those types that are assignable to U
 * 从T中提取可赋值给U的类型
 */
type Extract<T, U> = T extends U ? T : never;

Extract<Type, Union>,通过从Type中提取可分配给Union的所有联合成员来构造类型。取相交的

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

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; x: number }
  | { kind: "triangle"; x: number; y: number };

type T2 = Extract<Shape, { kind: "circle" }>
//  type T2 = {  kind: "circle";  radius: number;  }

NonNullable

/**
 * Exclude null and undefined from T
 * 从T类型中排除null和undefined类型
 */
type NonNullable<T> = T & {};

通过从Type中排除nullundefined来构造一个类型。

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

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

Parameters 函数参数类型

/**
 * Obtain the parameters of a function type in a tuple
 * 获取元组中函数类型的参数
 */
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

Parameters<Type>,从函数类型Type的形参中使用的类型构造元组类型。

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

type T0 = Parameters<() => string>; // type T0 = [] 没有参数,所以是空数组

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

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

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

typeof f1; // (arg: {  a: number;  b: string;  }) => void

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

type T5 = Parameters<never>; // type T5 = never

type T6 = Parameters<string>; // Type 'string' does not satisfy the constraint '(...args: any) => any'

type T7 = Parameters<Function>; // Type 'Function' does not satisfy the constraint '(...args: any) => any'.  Type 'Function' provides no match for the signature '(...args: any): any'.

ReturnType 函数返回值类型

/**
 * Obtain the return type of a function type
 */
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

得到函数的返回值类型

// @errors: 2344 2344
declare function f1(): { a: number; b: string };

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

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

type T2 = ReturnType<<T>() => T>; // type T2 = unknown

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

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

type T5 = ReturnType<any>; // type T5 = any

type T6 = ReturnType<never>; // type T6 = never

type T7 = ReturnType<string>; // Type 'string' does not satisfy the constraint '(...args: any) => any'.

type T8 = ReturnType<Function>; // Type 'Function' does not satisfy the constraint '(...args: any) => any'.  Type 'Function' provides no match for the signature '(...args: any): any'.

TupleToUnion 元组转集合

实现范型TupleToUnion<T>,它返回元组所有值的集合。

例如:

type Arr = ['1', '2', '3']

type Test = TupleToUnion<Arr> // '1' | '2' | '3'

实现方式:

type Arr = ['1', '2', '3']
// 实现
type TupleToUnion<T extends any[]> = T[number]

type Test = TupleToUnion<Arr> // '1' | '2' | '3'

const test: Test = '1'

元组转对象

将一个元组类型转换为对象类型,这个对象类型的键/值和元组中的元素对应。

例如:

const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

type result = TupleToObject<typeof tuple> // expected { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}