4
头图

contact us : Youdao technical team assistant : ydtech01 / mailbox : ydtech@rd.netease.com

This article is the second in the "Playing with TypeScript Tool Types" series, and contains the following parts:

  • Must read: extends conditional operator
  • Exclude<Type, ExcludeUnion>
  • Extract<Type, Union>
  • NonNullable<Type>
  • must read: tuple type
  • Parameters<Type>
  • ConstructorParameters<Type>
  • ReturnType<Type>
  • InstanceType<Type>

One. Must read: extends conditional operator

Because the extends keyword is involved in the subsequent source code, you need to master this part of the content in advance to understand the source code better and easier. You can refer to [[[译]TypeScript condition type]]( http://juejin.cn/post/6985463429502877726), students who are good in English recommend to read the original text directly.

2. Exclude<Type, ExcludeUnion>: Exclude

Exclude all members of the ExcludeUnion union type from Type, which can be understood as taking the difference set . The remaining part is returned as a new type.

2.1 Source code interpretation

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

How to understand T extends U? Never: T? determines whether each item in T can be assigned to type U, if it can, it returns never, if not, it returns the current item.

Next, we combine a specific example to understand this sentence.

2.2 Practical usage

// 排除一个具体的值

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

// 排除一种类型

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

Taking T0 as an example, how does the extends keyword work?


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"

If you cannot understand the equivalent logic here, I suggest reading the recommended [[[译]TypeScript Conditional Type] at the beginning
](https://juejin.cn/post/6985463429502877726)

3. Extract<Type, Union>: Extract

Select portions of both public and Union Type Type Type and returned as a new type, it can be understood as intersected .

3.1 Source code analysis

/**

 * Extract from T those types that are assignable to U

 */

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

Extract those types of type T that can be assigned to type U. Therefore and Exclude in the source code is that never is placed on the branch where the result of the conditional operation is false.

So if you understand Extract, then it is not complicated to understand Extract.

3.2 Practical usage

// 提取一个具体的值

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

// 提取一种类型

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

The processing logic of extends will not be repeated here.

4. NonNullable<Type>

Filter out null and undefined in Type, and return the remaining type as a new type. In fact, it is a special case of Exclude.

4.1 Source code analysis

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

It can be found that the source code is very similar to Exclude<T, U>, except that U is replaced by null | undefined. So the combination of Exclude<T, U> is still very easy to understand.

4.2 Practical usage

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

//  type T0 = string | number

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

//  type T1 = string[]

5. Must read: tuple type

Is a tuple type having a fixed number of elements and element types are determined array 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.

For example, this is a tuple type:

let x: [string, number];

When we access an element of a declared type, we can get the correct type check:


// OK

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

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

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

When we access a subscript that exceeds the length of the array, the types obtained are all undefined, and we will get an error message that the subscript has no accessible elements:

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

6. Parameters<Type>

Construct a tuple type based on the type of a function parameter. So the role of this tool type is get the type of function parameters .

6.1 Source code analysis

/**

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

There are two key parts in the source code:

  • T extends (...args: any) => any, which stipulates that T must be a function (except any and never), and the parameter is of type any, so the parameter can be of any type.
  • T extends (...args: infer P) => any? P: never, if T is a function type, then this extends takes the true branch, that is, returns a P type, and this P type is the type of the parameter args

If you can not understand the students can infer then recall

6.2 Practical usage

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

Here we need to pay attention to the situation of parameters passing in never and any, just know.

7. ConstructorParameters<Type>

Return the parameter type of the constructor as a tuple type. We already know how to get the parameter type of a function, namely Parameters . So if we want to get the parameter type of the constructor, we must first determine which one is the constructor, and then get the parameter type. The idea is this kind of idea, then how should it be implemented?

7.1 Source code analysis

/**

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

There are two points to pay attention to here:

  • The function modified by the abstract keyword is called abstract method, and abstract method can only appear in abstract class
  • new (...args: any) => any, this is the definition of the constructor

Therefore, the ConstructorParameters tool type is only effective for abstract classes, we don't need to care about never and any.

7.2 Practical usage

Here is a specific example for a more in-depth understanding. Take ErrorConstructor as an example:

interface ErrorConstructor {

  new(message?: string): Error;

  (message?: string): Error;

  readonly prototype: Error;

}

type T0 = ConstructorParameters<ErrorConstructor>;

How is ConstructorParameters handled here? is actually to enter the three properties of ErrorConstructor one by one into T extends abstract new (...args: infer P) => any? P: never;, the parameter type P is returned if the conditions are met.

type T0 = string

8. ReturnType<Type>

Get the return value type of the function. Combining with the implementation of the function parameter type we obtained in Parameters , we can easily implement this tool type by ourselves.

8.1 Source code analysis

/**

 * Obtain the return type of a function type

 */

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

The only difference between the implementation of Parameters<Type> is that the position of infer has moved from the position of the parameter to the position of the return value. This is still very understandable.

Note that the return value type is not a tuple type, because in Parameters<Type>, args is an array, so what is returned is a tuple type, and the return value of the function can be of any type.

8.2 Practical usage

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)

9. InstanceType<Type>

Get the return type of the constructor instance. That is, the return value type after new operator on the constructor. We already know how to get the type of the constructor parameter, so it is very convenient to deduce how to get the type of the instance.

9.1 Source code analysis

/**

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

It also moved the position of infer from the position of the parameter to the position of the return value.

9.2 Practical usage

Here we continue to take FunctionConstructor as an example:

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

Combined with new(...args: string[]): Function; we can see:

type T0 = Function;

You can try it yourself in the typescript playground for more situations.

X. Next preview

In the next article "Fun with TypeScript Tool Type (Part 2)", the following content will be included, so stay tuned:

ThisParameterType<Type>

OmitThisParameter<Type>

ThisType<Type>

Quick Jump


有道AI情报局
788 声望7.9k 粉丝