TypeScript 的类型断言

Photo by Pankaj Patel / Unsplash

还有这种骚操作?

enum METHOD {
  DELETE = 'DELETE',
  GET = 'GET',
  HEAD = 'GET',
  OPTIONS = 'OPTIONS',
  PATCH = 'PATCH',
  POST = 'POST',
  PUT = 'PUT'
}

type TEST = {
  [key in METHOD]: any;
};

非空断言

表明该值一定存在。

function test(param?: string[]): string {
  return param!.join(' ');
}

类型化

Partial

把 interface 所有属性变成可选:

interface Obj {
  a: number;
  b: string;
}

type OptionalObj = Partial<Obj>

// interface OptionalObj {
//   a?: number;
//   b?: string;
// }

Readonly

把 interface 所有属性变成 readonly:

interface Obj {
  a: number;
  b: string;
}

type ReadonlyObj = Readonly<Obj>

// interface ReadonlyObj {
//   readonly a: number;
//   readonly b: string;
// }

Pick

获取类型中的若干项作为新的类型。

interface T {
  a: string;
  b: number;
  c: boolean;
}

type OnlyAB = Pick<T, 'a' | 'b'>;

// interface OnlyAB {
//   a: string;
//   b: number;
// }

Record

该类型可以将 K 中所有的属性的值转化为 T 类型,源码实现如下:

// node_modules/typescript/lib/lib.es5.d.ts

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

可以根据 K 中的所有可能值来设置 key,以及 value 的类型,举个例子:

type T11 = Record<'a' | 'b' | 'c', Person>; // -> { a: Person; b: Person; c: Person; }

Exclude

Exclude 将某个类型中属于另一个的类型移除掉。

源码的实现:

// node_modules/typescript/lib/lib.es5.d.ts

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

以上语句的意思就是 如果 T 能赋值给 U 类型的话,那么就会返回 never 类型,否则返回 T,最终结果是将 T 中的某些属于 U 的类型移除掉,举个例子:

type T00 = Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>;  // -> 'b' | 'd'
复制代码

可以看到 T'a' | 'b' | 'c' | 'd' ,然后 U'a' | 'c' | 'f' ,返回的新类型就可以将 U 中的类型给移除掉,也就是 'b' | 'd' 了。

Extract

Extract 的作用是提取出 T 包含在 U 中的元素,换种更加贴近语义的说法就是从 T 中提取出 U,源码如下:

// node_modules/typescript/lib/lib.es5.d.ts

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

以上语句的意思就是 如果 T 能赋值给 U 类型的话,那么就会返回 T 类型,否则返回 never,最终结果是将 TU 中共有的属性提取出来,举个例子:

type T01 = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>;  // -> 'a' | 'c'
复制代码

可以看到 T'a' | 'b' | 'c' | 'd' ,然后 U'a' | 'c' | 'f' ,返回的新类型就可以将 TU 中共有的属性提取出来,也就是 'a' | 'c' 了。

ReturnType

获取函数返回值。

function abc(): 'a' | 'b' | 'c' {
  return 'a';
}

const def: () => 'd' | 'e' | 'f' = () => {
  return 'd';
};

type a = ReturnType<typeof abc>; // 'a' | 'b' | 'c'
type d = ReturnType<typeof def>; // 'd' | 'e' | 'f'

ThisType

这个类型是用于指定上下文对象类型的。

// node_modules/typescript/lib/lib.es5.d.ts

interface ThisType<T> { }
复制代码

可以看到声明中只有一个接口,没有任何的实现,说明这个类型是在 TS 源码层面支持的,而不是通过类型变换。

这类型怎么用呢,举个例子:

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

const obj: ThisType<Person> = {
  dosth() {
    this.name // string
  }
}
复制代码

这样的话,就可以指定 obj 里的所有方法里的上下文对象改成 Person 这个类型了。

InstanceType

该类型的作用是获取构造函数类型的实例类型。

源码实现:

// node_modules/typescript/lib/lib.es5.d.ts

type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any;
复制代码

看一下官方的例子:

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

type T20 = InstanceType<typeof C>;  // C
type T21 = InstanceType<any>;  // any
type T22 = InstanceType<never>;  // any
type T23 = InstanceType<string>;  // Error
type T24 = InstanceType<Function>;  // Error
复制代码

NonNullable

这个类型可以用来过滤类型中的 nullundefined 类型。

源码实现:

// node_modules/typescript/lib/lib.es5.d.ts

type NonNullable<T> = T extends null | undefined ? never : T;
复制代码

比如:

type T22 = string | number | null;
type T23 = NonNullable<T22>; // -> string | number;
复制代码

Parameters

该类型可以获得函数的参数类型组成的元组类型。

源码实现:

// node_modules/typescript/lib/lib.es5.d.ts

type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;
复制代码

举个栗子:

function foo(x: number): Array<number> {
  return [x];
}

type P = Parameters<typeof foo>; // -> [number]
复制代码

此时 P 的真实类型就是 foo 的参数组成的元组类型 [number]

ConstructorParameters

该类型的作用是获得类的参数类型组成的元组类型,源码:

// node_modules/typescript/lib/lib.es5.d.ts

type ConstructorParameters<T extends new (...args: any[]) => any> = T extends new (...args: infer P) => any ? P : never;
复制代码

举个栗子:

class Person {
  private firstName: string;
  private lastName: string;
  
  constructor(firstName: string, lastName: string) {
      this.firstName = firstName;
      this.lastName = lastName;
  }
}

type P = ConstructorParameters<typeof Person>; // -> [string, string]
复制代码

此时 P 就是 Personconstructor 的参数 firstNamelastName 的类型所组成的元组类型 [string, string]

Omit

有时候我们想要继承某个接口,但是又需要在新接口中将某个属性给 overwrite 掉,这时候通过 PickExclude 就可以组合出来 Omit,用来忽略对象某些属性功能:

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;

// 使用
type Foo = Omit<{name: string, age: number}, 'name'> // -> { age: number }
复制代码

Mutable

将 T 的所有属性的 readonly 移除:

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

PowerPartial

内置的 Partial 有个局限性,就是只支持处理第一层的属性,如果是嵌套多层的就没有效果了,不过可以如下自定义:

type PowerPartial<T> = {
    // 如果是 object,则递归类型
    [U in keyof T]?: T[U] extends object
      ? PowerPartial<T[U]>
      : T[U]
};
复制代码

Deferred

相同的属性名称,但使值是一个 Promise,而不是一个具体的值:

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

Proxify

T 的属性添加代理

type Proxify<T> = {
    [P in keyof T]: { get(): T[P]; set(v: T[P]): void }
};
复制代码

Depromise

抹去 Promise 外包装,获取泛型值。

declare function test(): Promise<string>;

type Temp = ReturnType<typeof test> extends Promise<infer R> ? R : ReturnType<typeof test>; // string

封装,可得:

declare function test(): Promise<string>;

// 初始封装
type PickPromise<T> = T extends Promise<infer R> ? R : T;
type Temp = PickPromise<ReturnType<typeof test>>; // string

// 完整封装,指明 T 是函数类型
type PickPromiseFromReturnType<T extends (...args: any) => any> = ReturnType<T> extends Promise<infer R> ? R : ReturnType<T>;
type Temp = PickPromiseFromReturnType<typeof test>; // string

IInfinity

IInfinity

大道虽简,知易行难。
CN