Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the infer keyword needed in Typescript?

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 ?

like image 665
CodyBugstein Avatar asked Feb 04 '20 23:02

CodyBugstein


People also ask

What does infer mean in TypeScript?

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.

How does type inference work?

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.

What does the in keyword do in TypeScript?

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.


2 Answers

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

like image 95
ford04 Avatar answered Sep 22 '22 23:09

ford04


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.

like image 31
kaya3 Avatar answered Sep 21 '22 23:09

kaya3