Menu

Inferência de tipos em TypeScript: quando anotar e quando não

O TypeScript deduz a maioria dos tipos a partir dos valores que você escreve. Veja o que ele infere para variáveis, let vs const, objetos, arrays e valores de retorno, como callbacks recebem seus tipos pelo contexto e onde você ainda precisa de uma anotação.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Inferência de tipos é o TypeScript deduzindo um tipo que você não escreveu. Inicialize uma variável e ela assume o tipo do valor; retorne um valor de uma função e o tipo de retorno vem junto. A maior parte do código TypeScript tem bem menos anotações do que as pessoas imaginam.

Os comentários mostram o que o editor exibe quando você passa o mouse sobre cada nome. A última linha é um erro de compilação (TS2322), mesmo que count nunca tenha sido anotado; // @ts-expect-error marca o erro como esperado para que o resto do bloco rode.

let vs const: alargamento de literais

Um const nunca muda, então o TypeScript dá a ele o tipo mais estreito: o próprio valor literal. Um let recebe o tipo mais amplo, porque pode ser reatribuído depois.

let a = "left";    // string
const b = "left";  // "left"
let c = 42;        // number
const d = 42;      // 42
const e = true;    // true

Isso importa quando uma função aceita só certos valores:

index.ts(7,6): error TS2345: Argument of type 'string' is not assignable to parameter of type '"left" | "right"'.

Corrija com const ou anotando a variável: let dir: "left" | "right" = "left". Os tipos literais têm sua própria página, tipos literais.

Objetos e arrays alargam seus membros

As propriedades de um objeto literal são mutáveis, então elas se alargam como let, mesmo quando o próprio objeto é const. Os elementos de arrays fazem o mesmo. as const torna tudo readonly e mantém os tipos literais.

Inferência do tipo de retorno

O tipo de retorno de uma função é inferido a partir das instruções return. Quando há várias, o resultado é a union de todas elas.

Tipos de retorno inferidos servem para a maioria das funções. Escreva o tipo de retorno explicitamente em funções exportadas ou públicas quando quiser que a assinatura seja um contrato: uma mudança dentro do corpo que retorne outro tipo passa a falhar na própria função, e não em algum ponto de chamada distante.

Tipagem contextual: callbacks recebem seus tipos

Parâmetros não são inferidos a partir de como a função é chamada. Há duas exceções. Um parâmetro com valor padrão assume o tipo desse valor (function retry(times = 3) faz de times um number). E uma função escrita onde um tipo de função já é esperado recebe os tipos dos parâmetros desse contexto, então parâmetros de callbacks não precisam de anotação.

O mesmo vale para forEach, filter, reduce, callbacks de Promise, handlers de eventos e qualquer função que você passe para um parâmetro com um tipo de função declarado.

Parâmetros precisam de anotação

Sem um valor padrão ou um contexto como esse, o TypeScript não olha para como a função é chamada para adivinhar os tipos dos parâmetros. Com strict ativado, um parâmetro sem anotação é o erro TS7006:

index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type.

Escreva function double(x: number) e o bloco imprime 42, com o tipo de retorno number inferido de x * 2.

Best common type

Para um array literal com valores de tipos diferentes, o TypeScript infere um array da union desses tipos. Ele não procura um tipo base comum que você não mencionou.

O array vazio é o caso que merece atenção. const list = [] começa como um array "em evolução", cujo tipo cresce a cada push; uma anotação, const list: string[] = [], deixa a intenção clara desde o início.

Quando anotar

SituaçãoAnotar?
Variável inicializada com um valorNão, o valor define o tipo
Variável declarada sem valor (let result;)Sim, let result: string;
Parâmetros de funçãoSim (exceto callbacks com contexto ou com valor padrão)
Tipo de retorno de uma função interna pequenaOpcional
Tipo de retorno de uma função exportada ou públicaRecomendado, como contrato
Array ou objeto vazio que você preenche depoisSim, const ids: number[] = []
O valor deve manter seus tipos literaisUse as const
O valor precisa corresponder a um tipo mas manter o próprio tipo inferidoUse satisfies

A forma mais rápida de ver o que foi inferido é passar o mouse sobre o nome no editor. Sem editor, atribua o valor a uma variável de tipo errado, como const probe: boolean = mixed;, e a mensagem de erro do compilador mostra o tipo inferido.

Perguntas frequentes

O que é inferência de tipos no TypeScript?

É o compilador deduzindo um tipo que você não escreveu. let count = 0 dá a count o tipo number, e uma função que retorna a + b com dois números recebe o tipo de retorno number. O tipo inferido é verificado exatamente como um tipo escrito.

Devo anotar todas as variáveis no TypeScript?

Não. Anote os parâmetros de funções (eles não são inferidos pelas chamadas, só por um valor padrão ou pelo contexto de um callback), os tipos de retorno de funções exportadas se você quer uma API estável e as variáveis declaradas sem valor. Deixe o TypeScript inferir o resto: const name: string = "Ada" repete o que o compilador já sabe.

Por que const infere um tipo literal e let infere string?

Um const nunca pode ser reatribuído, então const dir = "left" recebe o tipo literal "left". Um let poderia guardar depois qualquer outra string, então let dir = "left" é alargado para string. O mesmo alargamento acontece com as propriedades de um objeto literal, porque propriedades podem ser reatribuídas; as const impede isso.

Por que o TypeScript diz que um parâmetro tem implicitamente o tipo 'any'?

É o erro TS7006 de noImplicitAny, que faz parte de strict. O TypeScript não infere tipos de parâmetros a partir de como a função é chamada, então um parâmetro sem tipo seria any. Adicione uma anotação, function double(x: number), ou passe a função onde o tipo dela é conhecido pelo contexto, como um callback de map.

Como vejo o tipo que o TypeScript inferiu?

Passe o mouse sobre o nome no editor (o VS Code e a maioria dos editores mostram o tipo inferido em um tooltip). No código, atribuir o valor a uma variável de um tipo claramente errado faz o compilador mostrar o tipo inferido na mensagem de erro.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR