TypeScript: Variáveis e inferência de tipos no TypeScript

Última atualização: 2026-08-26

Um dos recursos mais poderosos do TypeScript é a “inferência de tipos” — você não precisa escrever anotações de tipo em todos os lugares; o compilador as infere automaticamente para você. No entanto, é preciso compreender as regras de inferência para escrever código que o compilador possa interpretar corretamente.

1. Declaração de variáveis: let, const e var

O TypeScript oferece suporte total às três formas de declarar variáveis no JavaScript, mas recomenda-se usar let e const:

(1) let — uma variável que pode ser reatribuída

TYPESCRIPT
let count = 0;
count = 1;        // ✅ let Variables can be reassigned

(2) const — uma constante que não pode ser reatribuída

TYPESCRIPT
const MAX_SIZE = 100;
MAX_SIZE = 200;   // ❌ Error:Cannot be assigned to a constant

(3) var — Âmbito da função (não recomendado)

TYPESCRIPT
var x = 1;        // ⚠️ Available but not recommended——var There are issues with variable hoisting and scope leakage.
📌 Ponto-chave: Sempre use let e const; não use var. Se for possível usar const, use const em vez disso (quando o valor permanecer inalterado); use let somente quando for necessário reatribuir o valor. Isso está totalmente de acordo com as melhores práticas do JavaScript.

(4) Anotações de tipo versus inferência de tipo

TYPESCRIPT
// Type Annotations——Tell me TypeScript What is the type of the variable?
let age: number = 25;

// Type Inference——TypeScript Automatically determine the type based on the initial value
let name = "Charlie";    // TypeScript Inferred as string
let active = true;    // TypeScript Inferred as boolean

Ambas as abordagens produzem o mesmo resultado, mas, quando um valor inicial é fornecido, recomenda-se omitir a anotação para que a inferência de tipos possa ocorrer.



2. Regras completas para inferência de tipos

A inferência de tipos não é “adivinhação” — ela segue regras claras. Somente ao compreender essas regras é que você poderá prever o comportamento do compilador.

(1) Regra 1: Inferência dos valores iniciais

Quando uma variável possui um valor inicial, o TypeScript infere seu tipo com base nesse valor inicial:

TYPESCRIPT
let message = "hello";   // Inferred as string
let count = 42;          // Inferred as number
let flag = true;         // Inferred as boolean
let items = [1, 2, 3];   // Inferred as number[]

(2) Regra 2: Inferência de tipo para literais const

Esse é um dos detalhes mais sutis da inferência de tipos do TypeScript — os resultados da inferência para let e const são diferentes:

TYPESCRIPT
// let Inferred as a wide type(widening)
let x = "hello";     // Inferred as string
let n = 42;          // Inferred as number
let b = true;        // Inferred as boolean

// const Inferred as an exact literal type(no widening)
const x2 = "hello";  // Inferred as "hello"(Literal types,No string)
const n2 = 42;       // Inferred as 42(Literal types,No number)
const b2 = true;     // Inferred as true(Literal types,No boolean)
💡 Por que foi projetado dessa forma? let A variável pode ser reatribuída, por isso infere-se que ela tenha um tipo mais amplo (um string pode conter qualquer string); const A variável permanece inalterada, por isso infere-se que ela tenha exatamente o tipo literal. Esse comportamento é chamado de ampliação.

(3) Regra 3: Se nenhum valor inicial for especificado, ele será inferido como any

Se uma variável for declarada sem um valor inicial ou uma anotação de tipo, o TypeScript a infere como any:

TYPESCRIPT
let something;        // Inferred as any(Danger!)
something = 42;       // any,Do not check
something = "hello";  // any,Do not check——Completely bypassed the type system
🔥 Erro comum: any desativa efetivamente a verificação de tipos, o que torna essa abordagem idêntica à programação em JavaScript. Quando o modo strict está ativado, variáveis sem valores iniciais geram um erro (é necessário especificar explicitamente o tipo ou fornecer um valor inicial).

(4) Regra 4: Melhor inferência de tipos pública

Ao inferir tipos a partir de várias fontes, o TypeScript procura o “melhor tipo comum” entre elas:

TYPESCRIPT
let arr = [1, "hello", true];
// Inferred as (number | string | boolean)[] —— Union Type Arrays

let arr2 = [1, 2, 3];
// Inferred as number[] —— All elements are of the same type,Inferred as an array of element types

▶ Exemplo: Experimento de inferência de tipos

TYPESCRIPT
// Experiment1:let vs const Differences in Inference
let dynamicText = "hello";
const fixedText = "hello";

dynamicText = "world";     // ✅ string Can accept another string
// fixedText = "world";    // ❌ const Cannot be reassigned

// Experiment2:Inference of Object Properties
let user = {
  name: "Charlie",
  age: 20
};
// Inferred as { name: string; age: number }
user.name = "Diana";        // ✅ string Properties can be assigned new values
user.age = 21;             // ✅ number Properties can be assigned new values

// Experiment3:const Object——The property can still be modified!
const config = {
  host: "localhost",
  port: 3000
};
config.host = "127.0.0.1";  // ✅ const Only reassigning the object itself is prohibited.,Non-freezing property
// config = { host: "x", port: 1 };  // ❌ You cannot reassign the entire object
▶ Experimente

Saída:

TEXT 📖 Somente leitura
// Executed successfully


3. Noções básicas sobre compatibilidade de tipos

A compatibilidade de tipos do TypeScript utiliza o “subtipagem estrutural” — os tipos são compatíveis desde que suas estruturas coincidam, sem que seja necessário que os nomes dos tipos sejam os mesmos.

(1) Compatibilidade de subtipos

TYPESCRIPT
let num: number = 42;
let big: bigint = 100n;

// num = big;    // ❌ bigint No number subtypes of
// big = num;    // ❌ number No bigint subtypes of

(2) Os tipos literais são subtipos dos tipos largos

TYPESCRIPT
let text: string = "hello";    // string Type
const literal: "hello" = "hello";  // Literal types "hello"

text = literal;   // ✅ "hello" is a string subtype of,Can be assigned a value
// literal = text; // ❌ string No "hello" subtypes of
📌 Analogia: A relação entre tipos literais e tipos amplos é semelhante à que existe entre “maçã” e “fruta” — uma maçã é um tipo de fruta, mas uma fruta não é necessariamente uma maçã.

(3) “qualquer” refere-se à “cápsula de fuga”

TYPESCRIPT
let a: any = "hello";
a = 42;          // ✅ any Can accept any type
a = true;        // ✅ any No tests will be performed

let b: string = a;  // ✅ any It can also be assigned to any type.(Danger!)
⚠️ Aviso: any viola a segurança de tipos e só deve ser usado nas seguintes situações: (1) prototipagem rápida, (2) migração de código JavaScript legado ou (3) como uma solução alternativa temporária quando uma biblioteca de terceiros não possui definições de tipos. any deve ser evitado no desenvolvimento normal.



4. Combinando asserções de tipo e inferência de tipo

Se o resultado da inferência não for o desejado, você pode substituí-lo por meio de uma asserção de tipo:

TYPESCRIPT
// Scene:DOM Type Inference for Elements
let input = document.getElementById("myInput");
// Inferred as HTMLElement | null——I don't know exactly what element it is.

let inputEl = document.getElementById("myInput") as HTMLInputElement;
// Assert that HTMLInputElement——Tell the compiler"I'm sure this is input Element"
💡 Dica: As asserções de tipo não realizam conversões em tempo de execução; são meramente anotações de tipo em tempo de compilação. Se uma asserção estiver incorreta, ainda assim ocorrerão problemas em tempo de execução. Isso será explicado em detalhes na Lição 18.



5. Estratégias de inferência no desenvolvimento prático

(1) Estratégia 1: Se você puder deduzir, não anote

TYPESCRIPT
// ❌ Redundancy
let name: string = "Charlie";
const MAX: number = 100;
function add(a: number, b: number): number { return a + b; }

// ✅ Concise
let name = "Charlie";
const MAX = 100;
function add(a: number, b: number) { return a + b; }  // The return type is inferred as number

(2) Estratégia 2: As interfaces e os aliases de tipo devem ser especificados

TYPESCRIPT
interface User {
  name: string;
  age: number;
}

// When an object literal is assigned to a typed variable,Inference+Type checking takes effect simultaneously
let user: User = { name: "Charlie", age: 20 };  // ✅ Structural Matching
let bad: User = { name: "Charlie" };             // ❌ Missing age Properties

(3) Estratégia 3: Identificar explicitamente expressões complexas

TYPESCRIPT
// Return Values of Complex Functions——It is recommended to explicitly label them.,Avoiding Fallacies in Reasoning
function getUserInfo(id: number): { name: string; age: number } | null {
  if (id <= 0) return null;
  return { name: "User" + id, age: 20 };
}

▶ Exemplo: Inferência na prática — Objetos de configuração

TYPESCRIPT
// Use as const assertion to make object properties read-only literal types
const THEME = {
  primary: "#4A90D9",
  secondary: "#7B51D9",
  fontSize: 14
} as const;

// Inference Results:
// { readonly primary: "#4A90D9"; readonly secondary: "#7B51D9"; readonly fontSize: 14 }
// Each property is of an exact literal type + readonly

// THEME.primary = "#000";  // ❌ readonly Properties cannot be modified
// THEME.fontSize = 16;     // ❌ readonly Properties cannot be modified

console.log(THEME.primary);   // "#4A90D9"
console.log(THEME.fontSize);  // 14
▶ Experimente

Saída:

TEXT 📖 Somente leitura
#4A90D9
14
💡 O objetivo de as const: Ele instrui o TypeScript a inferir o tipo literal mais preciso para o valor (sem ampliação) e a aplicar o modificador readonly. É comumente usado em cenários como a definição de configurações constantes e tipos de ação.



▶ Exemplo: Inferência de tipos com arrays e objetos

TYPESCRIPT
// Array inference — element type is inferred automatically
let scores = [90, 85, 92];           // number[]
let names = ["Alice", "Bob"];        // string[]
let mixed = [1, "two", true];       // (number | string | boolean)[]

// Object inference — each property gets its own type
let point = { x: 10, y: 20 };       // { x: number; y: number }
let user = { name: "Eve", active: true }; // { name: string; active: boolean }

// Nested inference
let config = {
  server: { host: "localhost", port: 3000 },
  debug: true
};
// { server: { host: string; port: number }; debug: boolean }

console.log(config.server.host);    // "localhost"
console.log(typeof mixed[0]);       // "number"
▶ Experimente

Saída:

TEXT 📖 Somente leitura
localhost
number

❓ Perguntas Frequentes

P: Quando é necessário especificar manualmente as anotações de tipo? R: Em três situações: (1) Parâmetros de função — quando não há valor inicial para a inferência de tipo; (2) Declarações de variáveis sem valores iniciais — caso contrário, o tipo é inferido como any; (3) Tipos de retorno que não correspondem às expectativas — quando o tipo inferido é muito amplo ou muito restrito, ele deve ser substituído.

P: Quais são as implicações práticas da diferença na inferência de tipos entre let e const? R: let é inferido como um tipo amplo (como string ou number), enquanto const é inferido como um tipo literal (como “hello” ou 42). Essa diferença afeta principalmente a correspondência entre tipos de união e tipos literais. Por exemplo, ao definir uma constante de string com const, o tipo é inferido como um literal exato, que pode ser usado como um discriminador para tipos de união.

P: Qual é a relação entre any e o TypeScript? É uma boa ideia usar any? R: any é a “saída de emergência de tipos” do TypeScript — ela desativa a verificação de tipos, revertendo efetivamente para o modo JavaScript. Você não deve usar any a menos que esteja migrando código legado ou contornando temporariamente um problema de tipo. Se não tiver certeza sobre um tipo, usar unknown (explicado em detalhes na Lição 5) é mais seguro do que usar any.

P: O que é as const? Em que difere do const comum? R: O const comum apenas impede que uma variável seja reatribuída; a inferência de tipo ainda permite o alargamento (por exemplo, const x = "hi" é inferido como "hi", mas as propriedades do objeto permanecem do tipo mais amplo). as const é uma asserção de tipo que faz com que todos os níveis sejam inferidos como literais mais readonly. Somente usando as const com objetos ou matrizes é possível obter um comportamento verdadeiramente “somente leitura” profundo.

📖 Resumo

📝 Exercícios

  1. Exercício básico (Dificuldade ⭐): Declare uma variável de string usando let e outra usando const, e imprima seus valores. Em seguida, tente reatribuir valores a elas e observe como o TypeScript lida com o erro.
  2. Problema avançado (Dificuldade ⭐⭐): Defina um objeto de configuração as const chamado COLORS que contenha três valores hexadecimais de cor: vermelho, verde e azul. Em seguida, tente modificar uma das cores e observe a mensagem de erro gerada pela restrição readonly.
  3. Desafio (Dificuldade: ⭐⭐⭐): Escreva uma função createUser(name: string, age: number) sem especificar um tipo de retorno, permitindo que o TypeScript infira o tipo de retorno. Em seguida, use uma variável tipada fora da função para receber o valor de retorno e verifique se a inferência está correta.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%