The Infer keyword is used for type inference in conditions.
Typescript official website also took ReturnType
illustrate its role:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
Understood as follows: If T
inherited extends (...args: any[]) => any
type, type of return R
, otherwise any
. What is R
R
is defined in extends (...args: any[]) => infer R
, that is, R is derived from the input parameter type.
intensive reading
We can understand infer
from two perspectives, namely the demand perspective and the design perspective.
Understanding infer from a demand perspective
The realization of infer
must be a requirement behind it, which cannot be met by ordinary Typescript capabilities.
Imagine such a scenario: implement a function, receive an array, and return the first item.
We cannot use generics to describe this type of derivation, because generic types are a whole, and what we want to return is one of the T[0]
parameters. We cannot get the first type by writing like 06125c0538a03e:
function xxx<T>(...args: T[]): T[0]
In fact, it is reasonable not to support this writing method, because this time is to get the first item type. If T
is an object, we want to return the return value type of the Key onChange
Therefore, a new syntax must be used at this time, which is infer
.
Understanding infer from a design perspective
From the point of view of the type inference function, the generic function is very powerful. We can use the generic to describe the type passed in when calling, and describe it in the type expression in advance:
function xxx<T>(value: T): { result: T }
But we found that T
too integrated, and we do not yet have the ability to pick subtypes from it. That it is, for xxx<{label: string}>
this scenario, T = {label: string}
, but we can not R
defined as {label: R}
this position, because generics are a whole can not be split.
And in fact, for the sake of type safety, we cannot allow users to describe any type position. If the incoming type structure is not {label: xxx}
but a callback () => void
, isn't the subtype derivation built in the wrong environment? So considering that you want to get {label: infer R}
, first, the parameters must have {label: xxx}
, so it is just right to combine infer
with conditional judgment T extends ? A : B
, namely:
type GetLabelTypeFromObject<T> = T extends ? { label: infer R } ? R : never
type Result = GetLabelTypeFromObject<{ label: string }>;
// type Result = string
That is, if T
follows { label: any }
, then I can replace any variable position in this structure with infer xxx
. If the incoming type meets this structure (judging by the TS static analysis link), I can continue to derive based on this structure, so in the derivation In the process, we can use the variable type inferred by infer xxx
Looking back at the first requirement, you can use infer
achieve the first parameter type:
type GetFirstParamType<T> = T extends ? (...args: infer R) => any ? R[0] : never
It can be understood that if T
satisfies (...args: any) => any
this time, and we use infer R
indicate that R
refers to the first any
runtime type, then the type returned by the entire function is R
. If T
not met (...args: any) => any
this structure, such as GetFirstParamType<number>
, simply out of the question that this is derived directly back never
type reveal all the details, of course, you can also customize such any
any type of class.
Overview
After we understand the infer
, we combine conditional infer understand the examples in this article, which will help deepen memory.
type ArrayElementType<T> = T extends (infer E)[] ? E : T;
// type of item1 is `number`
type item1 = ArrayElementType<number[]>;
// type of item1 is `{name: string}`
type item2 = ArrayElementType<{ name: string }>;
As you can see, ArrayElementType
uses conditional inference and infer
to express such a logic: if the T
type is an array, and we define each item of the array as E
type, then the return type is E
, otherwise it is the overall type T
itself.
So for item1
is to meet the structure, so it returns number
, and item2
does not meet the structure, so the type itself is returned.
In particular, what does the following example return?
type item3 = ArrayElementType<[number, string]>;
The answer is number | string
. The reason is that we use multiple infer E
( (infer E)[]
equivalent to [infer E, infer E]...
is that multiple variables point to the same type pronoun E
number
and string
at the same time, so it can be understood as E
when number
is string
, so it can be understood as 06125c0538a350 Or relationship, this is covariance.
What if it is a function parameter?
type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
? U : never
type T21 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // string & number
It is found that the result is string & number
, which is inverted. But this example is the same U
sometimes as string
sometimes as number
Yeah, why is the relationship and, instead, or do?
In fact, covariance or contravariance is related to the position of the infer
In TypeScript, the return value types of objects, classes, arrays, and functions are all covariant, and the parameter types of functions are contravariant relationships, so infer
position of 06125c0538a3c6 is on the function parameter, the contravariant principle will be followed.
Inversion and covariation:
- Covariant (co-variant): Type convergence.
- Contra-variant: Type divergence.
You can open another article on the in-depth topic of inverter and coordination change, and I won’t go into infer
here. It’s enough to understand 06125c0538a479.
Summarize
infer
keyword gives us the ability to expand the generic structure in depth, pick any type in it, and use it as a temporary variable for the final return type.
For Typescript type programming, the biggest problem is that you want to achieve an effect but you don’t know what syntax to use. infer
will inevitably come in handy in most complex type deduction scenarios, so you encounter difficulties At the time, you can think about whether you can use infer
solve the problem.
The discussion address is: Intensive Reading of "Typescript infer Keywords" · Issue #346 · dt-fe/weekly
If you want to participate in the discussion, please click here , a new theme every week, weekend or Monday. Front-end intensive reading-to help you filter reliable content.
Follow front-end intensive reading WeChat public
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
Copyright statement: Freely reprinted-non-commercial-non-derivative-keep the signature ( Creative Commons 3.0 License )
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。