Type infer is used as a substatement of the extends condition type and was introduced in TS2.8. (Check out TypeScript extends condition types previously shared if you’re not familiar with them.)

The written form

Infer uses infer to declare a type variable that takes effect when the condition type is determined to true, for example:

type ExtractSelf<T> = T extends (infer U) ? U : T;

type T1 = ExtractSelf<string>;        // string
type T2 = ExtractSelf<(a)= > void>;    // () => void
type T3 = ExtractSelf<Date[] >;// Date[]
type T4 = ExtractSelf<{ a: string} >.// { a: string }
Copy the code

The infer U statement above declares a type variable U (which can be any letter or word) that resolves type T.

The resolution rule here is simple: U equals T, and return U. (depending on the execution priority, the infer syntax can be removed from the parentheses () and sometimes must be added, for example :(infer U)[])

Rule of inference

The above example is just a convenience for us to see it, the actual scenario would not be used this way, because it makes no sense. We update the above notation to fetch the type from the array:

type ExtractArrayItemType<T> = T extends (infer U)[] ? U : T;

// all conditions are false, return T
type T1 = ExtractArrayItemType<string>;         // string
type T2 = ExtractArrayItemType<(a)= > number>;   // () => number
type T4 = ExtractArrayItemType<{ a: string} >.// { a: string }

// If the condition is true, return U
type T3 = ExtractArrayItemType<Date[] >;// Date
Copy the code

By analyzing the format of T, infer U [] can be assigned a value of Date[], so the condition type is true. Then, based on the position of the variable U, we infer that U is equal to Date.

Let’s change it a bit to get the ReturnType of a function (similar to the official preset ReturnType advanced type) :

type ExtractReturnType<T> = T extends () => (infer U) ? U : T;

// If the condition is true, return U
type T1 = ExtractReturnType<(a)= > number>;   // number
Copy the code

As you can see from the above two examples, Infer can match any desired value type from the location of the type variable declared.

Infer the union type

If a variable of the same type exists in more than one location, and the data type is different at each location, it is inferred to be a joint type:

type ExtractAllType<T> = T extends { x: infer U, y: infer U } ? U : T;

type T1 = ExtractAllType<{ x: string, y: number} >.// string | number
Copy the code

The ExtractAllType

infer format here has fixed x and Y attributes. We can optimize it to accept any number:

type ExtractAllType<T> = T extends { [k: string]: infer U } ? U : T;

type T1 = ExtractAllType<{ x: string, y: number, z: boolean} >.// string | number | boolean
Copy the code

Now that we know about this feature, we can look at the function above to extract the type of an array. It can actually be used like this:

type ExtractArrayItemType<T> = T extends (infer U)[] ? U : T;

type ItemTypes = ExtractArrayItemType<[string.number>;// string | number
Copy the code

This implements the conversion of a tuple type to a union type.

Rules for function overloading

Overloading declared functions always gets the last declaration, but reformats the function using typeof function conversions:

declare function foo(x: string) :number;
declare function foo(x: number) :string;
declare function foo(x: string | number) :string | number;

type 1 = ReturnType<typeof foo>;  // string | number
Copy the code