Guia Completo de Tipos Primitivos, Literais e Type Inference em TypeScript Já leu

Tipos Primitivos em TypeScript Os tipos primitivos são a base de qualquer programa TypeScript. Diferentemente do JavaScript, onde você descobre o tipo em tempo de execução, TypeScript permite que você declare explicitamente qual tipo uma variável deve ter. Isso significa que o compilador vai verificar se você está usando aquela variável corretamente antes mesmo do código rodar. TypeScript herda os tipos primitivos do JavaScript e adiciona segurança de tipo sobre eles. Os principais tipos primitivos são: , , , , , e . Cada um deles tem características específicas e comportamentos bem definidos. Number O tipo representa qualquer número, seja inteiro ou decimal. Ao contrário de linguagens como Java ou C#, TypeScript não diferencia entre inteiros e floats — tudo é . Você também pode trabalhar com números em diferentes bases: hexadecimal, octal e binária. Isso é útil em contextos de manipulação de bits ou trabalho com flags. String O tipo representa texto. TypeScript aceita strings com aspas simples, duplas

Tipos Primitivos em TypeScript

Os tipos primitivos são a base de qualquer programa TypeScript. Diferentemente do JavaScript, onde você descobre o tipo em tempo de execução, TypeScript permite que você declare explicitamente qual tipo uma variável deve ter. Isso significa que o compilador vai verificar se você está usando aquela variável corretamente antes mesmo do código rodar.

TypeScript herda os tipos primitivos do JavaScript e adiciona segurança de tipo sobre eles. Os principais tipos primitivos são: number, string, boolean, null, undefined, bigint e symbol. Cada um deles tem características específicas e comportamentos bem definidos.

Number

O tipo number representa qualquer número, seja inteiro ou decimal. Ao contrário de linguagens como Java ou C#, TypeScript não diferencia entre inteiros e floats — tudo é number.

let idade: number = 28;
let altura: number = 1.75;
let temperatura: number = -5;
let infinito: number = Infinity;
let naoNumero: number = NaN;

// TypeScript vai reclamar disso:
let erro: number = "vinte e oito"; // Erro: Type 'string' is not assignable to type 'number'

Você também pode trabalhar com números em diferentes bases: hexadecimal, octal e binária. Isso é útil em contextos de manipulação de bits ou trabalho com flags.

let hexadecimal: number = 0xFF; // 255
let octal: number = 0o77;       // 63
let binario: number = 0b1111;   // 15

String

O tipo string representa texto. TypeScript aceita strings com aspas simples, duplas ou com template literals (backticks).

let nome: string = "João";
let sobrenome: string = 'Silva';
let mensagem: string = `Olá, ${nome} ${sobrenome}!`;

console.log(mensagem); // "Olá, João Silva!"

Template literals são particularmente poderosos porque permitem interpolar valores dentro da string usando ${}. Isso é muito melhor do que concatenar strings manualmente.

Boolean

O tipo boolean tem apenas dois valores possíveis: true e false. Parece simples, mas é fundamental em estruturas de controle como if, while e for.

let ativo: boolean = true;
let deletado: boolean = false;

let maiorDeIdade: boolean = idade >= 18;

if (maiorDeIdade) {
  console.log("Pode entrar na discoteca");
}

Um erro comum é tentar usar um valor não-booleano em um contexto que espera um boolean. TypeScript vai te proteger disso.

Null e Undefined

null e undefined são dois tipos especiais que costumam confundir iniciantes. undefined significa que uma variável foi declarada mas não recebeu um valor. null é um valor atribuído explicitamente para representar "nada" ou "ausência de valor".

let indefinido: undefined = undefined;
let nulo: null = null;

let nome: string; // undefined por padrão
console.log(nome); // undefined

let resultado: null = null; // você atribuiu null explicitamente

Na prática, você raramente vai anotar uma variável como undefined ou null. Mas é importante entender a diferença porque TypeScript tem recursos para lidar com ambos (como o operador de coalescência nula ??).

BigInt

O tipo bigint permite trabalhar com números inteiros arbitrariamente grandes. Números normais têm um limite (até 2^53 - 1), mas bigint não tem esse limite.

let numeroGrande: bigint = 9007199254740991n;
let muitoMaior: bigint = 9007199254740992n; // note o 'n' no final

let resultado: bigint = numeroGrande + 1n;
console.log(resultado); // 9007199254740992n

A letra n no final é obrigatória para indicar que é um bigint. Um detalhe importante: você não pode misturar number e bigint em operações.


Literais em TypeScript

Enquanto os tipos primitivos são genéricos (qualquer string é string), os literais são específicos. Um literal é um tipo que representa um valor exato. Por exemplo, a string "João" é um valor específico, e TypeScript permite criar um tipo que aceita apenas aquele valor.

Literal Types

Literal types são incrivelmente úteis quando você quer restringir os valores possíveis de uma variável. Ao invés de aceitar qualquer string, você pode aceitar apenas "ativo", "inativo" ou "pendente".

let status: "ativo" | "inativo" | "pendente" = "ativo";
status = "inativo"; // OK
status = "deletado";  // Erro: Type '"deletado"' is not assignable to type '"ativo" | "inativo" | "pendente"'

Você já deve ter visto a sintaxe | (union) ali — ela significa "ou". Então "ativo" | "inativo" | "pendente" significa "um desses três valores, não mais".

Essa é uma forma muito melhor de trabalhar do que simplesmente usar string. Se alguém passar um status inválido, o compilador vai avisar no desenvolvimento, não em produção.

Numeric Literals

Você também pode usar literais numéricos para representar valores exatos:

type StatusCode = 200 | 301 | 404 | 500;

let resposta: StatusCode = 200; // OK
let erro: StatusCode = 503;     // Erro: Type '503' is not assignable to type 'StatusCode'

Boolean Literals e Combinações

Existem casos onde você quer ser ainda mais específico:

type Sucesso = true;
type Falha = false;

let resultado: Sucesso = true;  // OK
let resultado2: Falha = false;  // OK
let resultado3: Sucesso = false; // Erro

Você também pode combinar literais de tipos diferentes:

type Resposta = "sim" | "não" | true | 42;

let escolha: Resposta = "sim";  // OK
let outra: Resposta = true;     // OK
let numero: Resposta = 42;      // OK
let invalida: Resposta = false; // Erro

Type Inference (Inferência de Tipo)

Type inference é o superpoder do TypeScript. Em muitos casos, você não precisa anotar tipos explicitamente — o compilador consegue deduzir qual tipo uma variável tem baseado no valor que você atribui a ela. Isso torna o código mais legível e reduz a quantidade de anotações.

Inferência Básica

Quando você cria uma variável com um valor inicial, TypeScript automaticamente infere o tipo:

let nome = "João";      // TypeScript infere: string
let idade = 28;         // TypeScript infere: number
let ativo = true;       // TypeScript infere: boolean

// Agora o editor vai avisar se você tentar fazer algo errado:
nome = 42;              // Erro: Type 'number' is not assignable to type 'string'
idade = "vinte e oito"; // Erro: Type 'string' is not assignable to type 'number'

Essa é a razão pela qual muitos desenvolvedores TypeScript experientes não colocam anotações em variáveis simples. A inferência já faz o trabalho.

Inferência em Funções

Você pode omitir a anotação do tipo de retorno em funções, e TypeScript vai inferir baseado no que você retorna:

function somar(a: number, b: number) {
  return a + b; // TypeScript infere que o retorno é: number
}

const resultado = somar(5, 3); // resultado tem tipo: number
console.log(resultado); // 8

Mesmo sem escrever function somar(...): number, o compilador sabe que essa função retorna um número. Porém, é uma boa prática anotar o retorno explicitamente para deixar claro para quem lê o código.

function somar(a: number, b: number): number {
  return a + b;
}

Inferência em Arrays e Objetos

Com arrays, TypeScript infere o tipo dos elementos:

let numeros = [1, 2, 3];
// TypeScript infere: number[]

numeros.push(4);   // OK
numeros.push("5"); // Erro: Argument of type 'string' is not assignable to parameter of type 'number'

Com objetos, a inferência funciona para cada propriedade:

let usuario = {
  nome: "João",
  idade: 28,
  ativo: true
};

// TypeScript infere:
// {
//   nome: string;
//   idade: number;
//   ativo: boolean;
// }

usuario.nome = "Maria";  // OK
usuario.idade = "trinta"; // Erro: Type 'string' is not assignable to type 'number'
usuario.email = "joao@example.com"; // Erro: Property 'email' does not exist on type...

Quando a Inferência Não é Suficiente

Há situações onde o TypeScript não consegue inferir o tipo corretamente, e você precisa ser explícito:

// Sem inicialização, TypeScript não consegue inferir
let valor;
valor = 42;
valor = "texto"; // OK, porque TypeScript assume 'any'

// Solução: anotar o tipo
let numero: number;
numero = 42;
numero = "texto"; // Erro

Outro caso é quando você trabalha com genéricos e valores abstratos:

function processar<T>(item: T): T {
  return item; // TypeScript sabe que retorna T
}

let resultado = processar("teste"); // resultado tem tipo: string
let outro = processar(42);          // outro tem tipo: number

Praticando: Cenário Real

Vamos juntar tudo isso em um exemplo prático. Imagine que você está construindo um sistema de gerenciamento de usuários:

type StatusUsuario = "ativo" | "inativo" | "bloqueado";
type TipoPermissao = "leitura" | "escrita" | "admin";

interface Usuario {
  id: number;
  nome: string;
  email: string;
  idade: number;
  ativo: boolean;
  status: StatusUsuario;
  permissoes: TipoPermissao[];
}

function criarUsuario(nome: string, email: string, idade: number): Usuario {
  return {
    id: Math.floor(Math.random() * 10000),
    nome,
    email,
    idade,
    ativo: true,
    status: "ativo",
    permissoes: ["leitura"]
  };
}

function atualizarStatus(usuario: Usuario, novoStatus: StatusUsuario): void {
  usuario.status = novoStatus;
  usuario.ativo = novoStatus !== "bloqueado";
}

// Usando as funções
const usuario1 = criarUsuario("João Silva", "joao@example.com", 28);
console.log(usuario1);
// {
//   id: 5432,
//   nome: 'João Silva',
//   email: 'joao@example.com',
//   idade: 28,
//   ativo: true,
//   status: 'ativo',
//   permissoes: [ 'leitura' ]
// }

atualizarStatus(usuario1, "bloqueado");
console.log(usuario1.ativo); // false

// Isso causaria erro:
atualizarStatus(usuario1, "suspenso"); // Erro: Argument of type '"suspenso"' is not assignable...
usuario1.status = "deletado";          // Erro: Type '"deletado"' is not assignable...

Veja como TypeScript protege você de erros:
1. Tipos primitivos garantem que idade seja sempre um número
2. Literal types garantem que status seja apenas um dos valores esperados
3. Type inference permite que funções como criarUsuario retornem o objeto correto sem repetição de código


Conclusão

Os três pilares que você deve levar para casa são:

  1. Tipos Primitivos são segurança: Ao anotar uma variável como number, string ou boolean, você está criando um contrato. TypeScript vai verificar esse contrato e avisar se algo não se encaixa. Isso previne bugs antes que chegem em produção.

  2. Literal Types são precisão: Quando você usa "ativo" | "inativo" ao invés de apenas string, você está sendo específico sobre quais valores são válidos. Isso torna seu código mais robusto e autoexplicativo.

  3. Type Inference é produtividade: Você não precisa anotar tudo. O TypeScript consegue deduzir tipos em muitas situações, permitindo que você escreva código limpo sem sacrificar segurança. Aprenda quando é seguro omitir anotações e quando deve ser explícito.


Referências


Artigos relacionados