Why did the Typescript folks create the infer
keyword? According to the documents, this is an example of how you would use it:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
I don't understand why this is needed. Why can't it just be:
type ReturnType<T> = T extends (...args: any[]) => R ? R : any;
Why doesn't this work? Why is the infer
keyword necessary ?
The infer keyword allows types to be extracted from conditions in conditional types. This is often used within Typescript's standard utility types. In the next lesson, we will gain a deep understanding of some of TypeScripts standard conditional utility types.
Type inference is the ability to automatically deduce, either partially or fully, the type of an expression at compile time. The compiler is often able to infer the type of a variable or the type signature of a function, without explicit type annotations having been given.
The in operator can be used to help TypeScript narrow the type of an object variable by its property name. It is arguably more useful than instanceof because it can be applied to any object structure.
With infer
, the compiler ensures that you have declared all type variables explicitly:
type MyType<T> = T extends infer R ? R : never; type T1 = MyType<{b: string}> // T1 is { b: string; }
Here we declare a new type variable R
in MyType
, which gets inferred from T
.
(Note, that infer
is always used within the extends
clause of a conditional type.)
Usage of undeclared type parameters now can result in a compile error:
type MyType2<T> = T extends R2 ? R2 : never; // error, R2 undeclared
Without infer
, the compiler wouldn't know, if you wanted to introduce an additional type variable R2
that is to be inferred (see first case), or if R2
is just an accidental typing error/typo. infer
exists to remove this ambiguity.
More precisely the compiler checks, if T
is assignable to R
, when infer
is omitted:
type R = { a: number } type MyType3<T> = T extends R ? R : never; // compare T with type R type T2 = MyType2<{b: string}> // T2 is never
Note, that infer R
shadows type references of an equally-named type declaration R
:
type R = { a: number } type MyType<T> = T extends infer R ? R : never; type T1 = MyType<{b: string}> // { b: string; }
Playground
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With