Como funciona o infer do Typescript
A palavra-chave infer
foi uma nova funcionalidade do Typescript 2.8. Isso é útil pra extrair um tipo de outra definição de tipo, e deve ser usado exclusivamente em tipos condicionais.
Vamos dar uma olhada num exemplo retirado da documentação oficial do site do Typescript:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
Num primeiro momento, eu não consegui entender isso direito. Por que a palavra-chave infer
é necessária se ela só retorna o mesmo tipo do valor retornado pela função? Bem, o ponto é que quando usamos infer
, o Typescript vai na verdade tentar inferir um tipo em R
. Se esse tipo existir, então a condição é satisfeita e este tipo é retornado. Caso contrário, any
será retornado:
type A = ReturnType<() => User>; // User
type B = ReturnType<() => number>; // number
type C = ReturnType<() => stirng>; // any
O ponto é que sem o infer
, essa sentença sempre vai retornar R
, independente de que tipo ele seja.
Múltiplos candidatos
Em alguns casos, é possível atribuir resultados de multiplas inferências a uma mesma variável. Nesses casos, o tipo resultante pode ser uma união ou interseção.
Quando eles estão em posições covariantes, o resultado é uma união:
type Foo<T> = T extends { a: infer U; b: infer U } ? U : never;
type FooProps = Foo<{ a: string; b: number }>; // string | number
Quando eles estão em posições contravariantes, o resultado é uma interseção:
type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
? U
: never;
type BarPropsParams = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // string & number
E quando eles estão em uma sobrecarga, a última assinatura é a que prevalece:
declare function foo(x: number): string;
declare function foo(x: string | number): string | number;
type FooReturnType = ReturnType<typeof foo>; // string | number
Conclusão
Pegou a ideia? Com infer é possível obter um tipo de uma sentença sem conhecê-lo. Isso pode ser bastante útil para bibliotecas, em cenários onde os tipos não são previamente conhecidos.