preface

Infer was a new keyword in typescript 2.8, a little more than two years after it was introduced in 3.9.3. Infer was used today

infer

Infer can infer the type to be inferred from an extends conditional statement

For example, in the document’s example, infer is used to infer the return value type of a function

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

type func = (a)= > number;
type variable = string;
type funcReturnType = ReturnType<func>; // funcReturnType is number
type varReturnType = ReturnType<variable>; // varReturnType The type is string
Copy the code

In this example, infer R represents the type of return value to be inferred, and returns the return value of the function if T is a function, or any otherwise

Just from this example, it’s hard to know what infer is used for. How many more examples do we need to see

Infer unpack

Infer can not only infer the return value, but also unpack. I think this is quite common

If I wanted to get the types of elements in an array, I did this before infer

type Ids = number[];
type Names = string[];

type Unpacked<T> = T extends Names ? string : T extends Ids ? number : T;

type idType = Unpacked<Ids>; // idType Indicates that the type is number
type nameType = Unpacked<Names>; // nameType 类型为string
Copy the code

Last time I wrote about 20 lines to get a bunch of different types of array elements, but infer makes it much easier to unpack

type Unpacked<T> = T extends (infer R)[] ? R : T;

type idType = Unpacked<Ids>; // idType Indicates that the type is number
type nameType = Unpacked<Names>; // nameType 类型为string
Copy the code

T extends (infer R)[]? R: T means that if T is an array of the type to be inferred, return the inferred type, otherwise return T

For example, if I want to obtain the XXX type of a Promise< XXX > type, I can’t think of any solution without using infer

type Response = Promise<number[] >;type Unpacked<T> = T extends Promise<infer R> ? R : T;

type resType = Unpacked<Response>; // resType is of type number[]
Copy the code

Infer indicates the type of association

Again, an example of an official document

type Foo<T> = T extends { a: infer U; b: infer U } ? U : never;

type T10 = Foo<{ a: string; b: string} >.// The T10 type is string
type T11 = Foo<{ a: string; b: number} >./ / T11 type string | number
Copy the code

It is convenient to convert tuples to the union type for the fact that a variable of the same type can be inferred to be of the union type if it has multiple inferred values

type ElementOf<T> = T extends (infer R)[] ? R : never;

type TTuple = [string.number];
type Union = ElementOf<TTuple>; / / the Union type string | number
Copy the code

The use of infer in React

Infer should always be used in React typescript sources

Let’s take useReducer, for example, if we use useReducer like this

const reducer = (x: number) = > x + 1;
const [state, dispatch] = useReducer(reducer, ' ');
// Argument of type "" is not assignable to parameter of type 'number'.
Copy the code

So here useReducer is going to report a type error saying, “” Can’t assign to type number

How do we determine the state type based on the reducer function type in React?

View the definition of userReducer

Function useReducer<R extends Reducer<any, any>, I>(Reducer: R, // ReducerState I & ReducerState<R>, initializer: (arg: I & ReducerState<R>) => ReducerState<R> ): [ReducerState<R>, Dispatch<ReducerAction<R>>]; // infer Type ReducerState<R extends Reducer<any, any>> = R extends Reducer<infer S, any>? S : never; // Reducer type type <S, A> = (prevState: S, action: A) => S;Copy the code

Infer is used to infer the state parameter type in the Reducer function

The problem I encountered today

Today we use ant-design-chart, but the definition of Ref is not exported from the library, so we have to take it by ourselves

/ / known
type ref = React.MutableRefObject<G2plotStackedBar | undefined>;
/ / please???
constchartRef = useRef<??? > ()Copy the code

With the above learning, it is easy to do here. Just take out the contents of React.MutableRefObject and infer

/ / infer inference
type ChartRef<T> = T extends React.MutableRefObject<infer P> ? P : never;
                                                    
const chartRef = useRef<ChartRef<ref>>()
Copy the code

conclusion

Infer can be very useful, and advanced features must be understood if you want to get away from just writing tape type javascript

I may have seen Infer a year ago. But I haven’t studied well because I am lazy and my proficiency is not good enough. I feel different when I study again this year.

I would like to recommend another good article. After reading this article, I learned infer well. The article is more complicated

Vue3 follows yuyuxi’s example of TypeScript’s Ref type being implemented from zero

As an aside, let’s share a more complicated exercise

I won’t post the original, but you can see it on Github here

Share my thoughts

  1. So let’s get the name of the function and passextendsThe keyword is used to determine whether a function is a functionnever, and finally use the mapping type[keyof T]To get the union type of the key name, becauseneverTypes associated with any type group are filtered outneverSo nature ruled it outnever
  2. Just useinferPush hard

The solution is as follows:

type EffectModuleFuncName = {
  [K in keyof EffectModule]: EffectModule[K] extends Function ? K : never;
}[keyof EffectModule];

type UnPackedPromise<T> = T extends Promise<infer P> ? P : T;

type EffectModuleFunc<T> = T extends (params: infer P) => infer U
  ? P extends Promise<infer R>
    ? (v: R) = > UnPackedPromise<U>
    : P extends Action<infer X>
    ? (v: X) = > UnPackedPromise<U>
    : never
  : never;

// Change the type of Connect to make connected the expected type
type Connect = (
  module: EffectModule,
) => { [K in EffectModuleFuncName]: EffectModuleFunc<EffectModule[K]> };
Copy the code

Also do not know oneself write right, always feel strange, can discuss


References:

  1. TypeScript document
  2. Learn more about TypeScript

Finally, I wish you all good health and smooth work!

Welcome to follow my public account ~