Why is the infer keyword needed in Typescript?
With infer
, the compiler ensures that you have declared all type variables explicitly. E.g. type variables can be defined:
- after
infer
within theextends
clause of a conditional type →R
inT extends (...args: any[]) => infer R
- on the left-hand side of the type declaration →
T
inReturnType<T> = ...
- as part of a mapped type →
K
intype Mapped<T> = { [K in keyof T]: ... }
Usage of undeclared type parameters now can result in a compile error. Without infer
, the compiler wouldn't know, if you wanted to introduce an additional type variable (say R
) that is to be inferred, or if R
is just an accidental typing error/typo.
type R = { a: number }type MyType<T> = T extends infer R ? R : never; // infer new variable R from Ttype MyType2<T> = T extends R ? R : never; // compare T with above type Rtype MyType3<T> = T extends R2 ? R2 : never; // error, R2 undeclaredtype T1 = MyType<{b: string}> // T1 is { b: string; }type T2 = MyType2<{b: string}> // T2 is never
Consider the following code:
interface Example { foo: string}type GenericExample<T> = T extends Examlep ? 'foo' : 'bar';
This code should result in a compilation error, because Examlep
is spelled incorrectly; there is no type named Examlep
, and obviously the programmer meant to write Example
here.
Now imagine the infer
keyword is not needed in an extends
clause of a conditional type. Then the above code would not give a compilation error; it would see that there is no type named Examlep
, infer what type it is, and then (since Examlep
has no constraints) observe that T
does indeed extend Examlep
for the inferred type.
In that case, GenericExample<T>
would always be 'foo'
regardless of what T
is, and there would be no compilation error to inform the programmer about the mistake. This would be the wrong thing for the compiler to do, almost all of the time.
The infer keyword allows you to deduce a type from another type within a conditional type. Here’s an example:
type UnpackArrayType<T> = T extends (infer R)[] ? R: T;type t1 = UnpackArrayType<number[]>; // t1 is number
UnpackArrayType is a conditional type. It is read as “If T is a sub-type of (infer R)[] , return R. Otherwise, return T”.
For type alias t1, the condition in UnpackArrayType is true because number[] matches with (infer R)[]. As the result of the infer process, the type variable R is inferred to be number type, and returned from the true branch. Infer is there to tell compiler that a new type variable R is declared within
the scope of UnpackArrayType.type t2 = UnpackArrayType<string>; //t2 is string
For t2 , the condition in UnpackArrayType is false as the string type does not match with(infer R)[] , so it is returned as string.For more information, look at this article.https://javascript.plainenglish.io/typescript-infer-keyword-explained-76f4a7208cb0?sk=082cf733b7fc66228c1373ba63d83187