Play typescript tool type (medium)

Time:2022-5-14

Play typescript tool type (medium)

contact usYoudao technical team assistant:ydtech01 / mailbox[email protected]

This article is the second in the series “playing with typescript tool types”, which includes the following parts:

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

I Required: extends conditional operator

Because the extensions keyword is involved in the subsequent source code, you need to master this part in advance in order to better and easier understand the source code. You can refer to [[[translation] typescript condition type]](http://juejin.cn/post/6985463…), students with good English are recommended to read the original text directly.

II Exclude < type, excludeunion >: exclude

Excluding all members of the excludeunion union union type from the type can be understood asTake difference set。 The rest is returned as a new type.

2.1 source code interpretation

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

How to understand t extensions u? Never: what about t?Judge whether each item in t can be assigned to type u. if so, return never. If not, return the current item.

Next, let’s understand this sentence with a specific example.

2.2 Practical usage

//Exclude a specific value

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

//Exclude a type

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"

//Equivalent to

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

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

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

//Equivalent to

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

//Equivalent to

type T0 = "b" | "c"

If you cannot understand the equivalence logic here, it is recommended to read the [[[translation] typescript condition type] recommended at the beginning
](https://juejin.cn/post/698546…)

III Extract < type, union >: extract

Select the public part of both type and union type and return it as a new type, which can be understood asintersect

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 the types of type t that can be assigned to type U. thereforeThe difference between the source code and exclude here is that never is placed on the branch whose conditional operation result is false.

Therefore, if you understand exclude, it is not complicated to understand extract.

3.2 practical usage

//Extract a specific value

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

//Extract a type

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

The processing logic of extends will not be repeated here.

IV NonNullable<Type>

Filter out null and undefined in the type, and return the remaining types 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 it is very similar to the source code of exclude < T, u >, except that u is replaced with null | undefined. Therefore, it is easy to understand in combination with exclude < T, u >.

4.2 practical usage

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

//  type T0 = string | number

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

//  type T1 = string[]

V Required reading: tuple type tuple type

A tuple type is one that hasFixed quantity elementandElement types are determinedArray type of.

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 elements with declared types, 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 subscripts that exceed the length of the array, the obtained types are undefined, and we will get an error prompt that there are no accessible elements in the access subscript:

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

Vi Parameters<Type>

Construct a tuple type based on the type of a function parameter. So the function of this tool type isGets the type of the function parameter

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 specifies that t must be a function (except any and never). 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, the extensions will take the true branch, that is, return a p type, which is the type of parameter args

If you can’t understand infer, you canLook back

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, you need to pay attention to the passing of parameters into never and any.

VII 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, that isParameters。 So if we want to get the parameter type of the constructor, we must first determine which constructor is, and then get the parameter type. The idea is this idea, so how should we implement it?

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;

Here are two points to focus on:

  • The function modified by the abstract keyword is called an abstract method, andAbstract methods can only appear in abstract classes
  • New (… Args: any) = > any, which is the definition of the constructor

So,ConstructorParametersTool types only work for abstract classes. We don’t need to care about never and any.

7.2 practical usage

Let’s take errorconstructor as an example:

interface ErrorConstructor {

  new(message?: string): Error;

  (message?: string): Error;

  readonly prototype: Error;

}

type T0 = ConstructorParameters<ErrorConstructor>;

How are constructorparameters handled here?In fact, it is to substitute the three attributes of errorconstructor into t extends Abstract new (… Args: information p) = > any? P : never; , If the conditions are met, the parameter type P is returned.

type T0 = string

VIII ReturnType<Type>

Gets the return value type of the function. Combine us inParametersGet the implementation of function parameter type, and you can easily implement this tool type yourself.

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 from the implementation of parameters < type > is that the position of infer moves from the parameter position to the return value position. It’s easy to understand.

Note that the return value type is not a tuple type, because in parameters < type >, args is an array, so it returns 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>;

//The type "string" does not satisfy the constraint "(... Args: any) = > any". ts(2344)

type T8 = ReturnType<Function>;

//Type 'function' does not satisfy constraint '(... Args: any) = > any'.

//The content provided by type 'function' does not match the signature '(... Args: any): any'. ts(2344)

IX InstanceType<Type>

Gets the return type of the constructor instance. That is, call the constructornewThe return value type after the operator. We already know how to get the type of constructor parameters, so it is convenient to deduce how to get the type of 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 moves the position of infer from the parameter position to the return value position.

9.2 practical usage

Here we continue to take the 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>;

Combine new (… Args: String []): function; It can be seen that:

type T0 = Function;

For more information, you can try it yourself in typescript playground.

X Next forecast

In the next “playing with typescript tool types (2)”, we will include the following contents. Please look forward to it:

ThisParameterType<Type>

OmitThisParameter<Type>

ThisType<Type>

Quick jump