关于网易:玩转TypeScript工具类型中

28次阅读

共计 5917 个字符,预计需要花费 15 分钟才能阅读完成。

分割咱们 有道技术团队助手 :ydtech01 / 邮箱:ydtech@rd.netease.com

本文是《玩转 TypeScript 工具类型》系列的第二篇,蕴含了如下几局部内容:

  • 必读:extends 条件运算符
  • Exclude<Type, ExcludeUnion>
  • Extract<Type, Union>
  • NonNullable<Type>
  • 必读:tuple type 元组类型
  • Parameters<Type>
  • ConstructorParameters<Type>
  • ReturnType<Type>
  • InstanceType<Type>

一. 必读:extends 条件运算符

因为后续的源码中波及到了 extends 关键字,所以须要先提前把握这部分内容能力更好更容易的了解源码。能够参考[[[译]TypeScript 条件类型]](http://juejin.cn/post/6985463…),英语好的同学举荐间接看原文。

二. Exclude<Type, ExcludeUnion>:排除

将 ExcludeUnion 联结类型的所有成员从 Type 中排除,能够了解为 取差集。残余的局部返回作为一个新类型。

2.1 源码解读

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

如何了解 T extends U ? never : T 呢?判断 T 中的每一项是否能够赋值给类型 U,如果能够,就返回 never,如果不能够,就返回以后这项。

接下来咱们联合一个具体的例子来了解一下这句话。

2.2 实战用法

// 排除一个具体的值

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

// 排除一种类型

type T1 = Exclude<string | number | (() => void), Function>

以 T0 为例,extends 关键字是如何发挥作用的呢?


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

// 等价于

type T0 = "a" extends "a" ? never : "a" 

  | "b" extends "a" ? never : "b"

  | "c" extends "a" ? never : "c"

// 等价于

type T0 = never | "b" | "c"

// 等价于

type T0 = "b" | "c"

如果无奈了解这里的等价逻辑,倡议浏览结尾举荐的[[[译]TypeScript 条件类型]
](https://juejin.cn/post/698546…)

三. Extract<Type, Union>:提取

选取 Type 类型和 Union 类型两者的公共局部并返回为一个新类型,能够了解为 取交加

3.1 源码解析

/**

 * Extract from T those types that are assignable to U

 */

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

提取出类型 T 中那些能赋值给类型 U 的类型。所以 这里源码上和 Exclude 的区别就在于 never 放在了条件运算后果为 false 的分支上。

所以如果了解了 Exclude,再了解 Extract 就不简单了。

3.2 实战用法

// 提取一个具体的值

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

// 提取一种类型

type T1 = Extract<string | number | (() => void), Function>

这里就不再反复阐明 extends 的解决逻辑了。

四. NonNullable<Type>

过滤掉 Type 中的 null 和 undefined,残余的类型作为一个新类型返回。其实就是 Exclude 的一种非凡状况。

4.1 源码解析

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

能够发现和 Exclude<T, U> 的源码十分像,只是把 U 换成了 null | undefined。所以联合 Exclude<T, U> 还是很好了解的。

4.2 实战用法

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

//  type T0 = string | number

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

//  type T1 = string[]

五. 必读:tuple type 元组类型

元组类型就是一个具备 固定数量元素 元素类型都确定 的数组类型。

Tuple types allow you to express an array with a fixed number of elements whose types are known, but need not be the same.

例如这就是一个元组类型:

let x: [string, number];

当咱们拜访已申明类型的元素时,能够取得正确的类型查看:


// OK

console.log(x[0].substring(1));

// Property 'substring' does not exist on type 'number'.

console.log(x[1].substring(1));

当咱们拜访超出数组长度的下标时,取得的类型都是 undefined,并且会取得一个拜访下标无可拜访元素的谬误提醒:

let x: [string, number];

x = ["hello", 10]; // OK

// Type '"world"' is not assignable to type 'undefined'.

// Tuple type '[string, number]' of length '2' has no element at index '3'.

x[3] = "world";

// Object is possibly 'undefined'.

// Tuple type '[string, number]' of length '2' has no element at index '5'.

console.log(x[5].toString());

六. Parameters<Type>

基于一个函数参数的类型结构一个元组类型(tuple type)。所以这个工具类型的作用就是 获取函数参数的类型

6.1 源码解析

/**

 * 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;

源码中重点有两局部:

  • T extends (…args: any) => any,规定了 T 必须是一个函数(any 和 never 除外),参数是 any 类型,所以参数能够是任意类型。
  • T extends (…args: infer P) => any ? P : never,如果 T 是函数类型,那么这个 extends 走的就是 true 分支,也就是返回一个 P 类型,而这个 P 类型就是参数 args 的类型

如果对 infer 无奈了解的同学能够再回顾一下

6.2 实战用法

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;}]

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'.

这里须要关注一下 Parameters 传入 never 和 any 的状况,晓得即可。

七. ConstructorParameters<Type>

把构造函数的参数类型作为一个元组类型返回。咱们曾经晓得如何获取一个函数的参数类型,即 Parameters。那么如果咱们要获取构造函数的参数类型,那么首先先要判断出哪一个是构造函数,而后获取参数类型即可,思路是这么个思路,那么应该怎么实现呢?

7.1 源码解析

/**

 * Obtain the parameters of a constructor function type in a tuple

 */

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

这里有两个须要关注的点:

  • abstract 关键字润饰的函数叫形象办法,而 形象办法只能呈现在抽象类中
  • new (…args: any) => any,这就是对构造函数的定义

所以,ConstructorParameters工具类型只是针对抽象类来施展成果的,never 和 any 咱们不须要关怀。

7.2 实战用法

这里联合一个具体的例子,深刻理解一下,就以 ErrorConstructor 为例:

interface ErrorConstructor {new(message?: string): Error;

  (message?: string): Error;

  readonly prototype: Error;

}

type T0 = ConstructorParameters<ErrorConstructor>;

这里 ConstructorParameters 是如何解决的呢?其实就是对 ErrorConstructor 的三条属性逐个代入 T extends abstract new (…args: infer P) => any ? P : never;,满足条件的就返回参数类型 P。

type T0 = string

八. ReturnType<Type>

获取函数的返回值类型。联合咱们在 Parameters 获取函数参数类型的实现,能够很容易的本人实现出这个工具类型。

8.1 源码解析

/**

 * Obtain the return type of a function type

 */

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

和 Parameters<Type> 实现的惟一的区别就是 infer 的地位从参数地位跑到了返回值地位。这还是很好了解的。

留神返回值类型不是元组类型,因为在 Parameters<Type> 中,args 是一个数组,所以返回的是一个元组类型,而函数的返回值能够是任意类型的。

8.2 实战用法

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>;

//  类型“string”不满足束缚“(...args: any) => any”。ts(2344)

type T8 = ReturnType<Function>;

//  类型“Function”不满足束缚“(...args: any) => any”。//   类型“Function”提供的内容与签名“(...args: any): any”不匹配。ts(2344)

九. InstanceType<Type>

获取构造函数实例的返回类型。也就是对结构函数调用 new 操作符后的返回值类型。咱们曾经晓得了如何获取结构函数参数的类型,所以很不便的就能够推导出如何获取实例的类型。

9.1 源码解析

/**

 * Obtain the return type of a constructor function type

 */

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

也是把 infer 的地位从参数地位移到了返回值地位。

9.2 实战用法

这里咱们持续以 FunctionConstructor 为例:

interface FunctionConstructor {

  /**

   * Creates a new function.

   * @param args A list of arguments the function accepts.

   */

  new(...args: string[]): Function;

  (...args: string[]): Function;

  readonly prototype: Function;

}

type T0 = InstanceType<FunctionConstructor>;

联合 new(…args: string[]): Function; 可知:

type T0 = Function;

更多状况能够在 typescript playground 中本人尝试一下。

十. 下回预报

在下一篇《玩转 TypeScript 工具类型(下)》里,将会包含如下内容,敬请期待:

ThisParameterType<Type>

OmitThisParameter<Type>

ThisType<Type>

快捷跳转

  • 玩转 TypeScript 工具类型(上)

正文完
 0