Tipos básicos: string, number, boolean, any, unknown, never

1. Introdução aos tipos primitivos no TypeScript

O TypeScript estende o JavaScript adicionando um sistema de tipos estáticos. Isso significa que você pode declarar explicitamente que tipo de valor uma variável deve armazenar, e o compilador verificará se você está usando esse valor corretamente ao longo do código. Os tipos básicos — string, number, boolean, any, unknown e never — formam a fundação sobre a qual todos os outros tipos são construídos.

O TypeScript possui duas maneiras de lidar com tipos: inferência de tipos e anotação explícita. Na inferência, o TypeScript deduz automaticamente o tipo com base no valor atribuído. Na anotação explícita, você declara o tipo manualmente usando : após o nome da variável.

// Inferência: TypeScript deduz que nome é string
let nome = "Maria";

// Anotação explícita: declaramos que idade é number
let idade: number = 30;

Os benefícios são imediatos: segurança contra erros de tipo, intellisense aprimorado no editor e documentação viva que se mantém sincronizada com o código.

2. string, number e boolean: os pilares

string

O tipo string representa valores textuais. Você pode usar aspas simples, duplas ou template literals.

let saudacao: string = "Olá, mundo!";
let nome: string = 'João';
let mensagem: string = `Bem-vindo, ${nome}!`;

number

O tipo number cobre inteiros, floats, NaN e Infinity. O TypeScript também suporta BigInt para números muito grandes, mas é um tipo separado.

let inteiro: number = 42;
let decimal: number = 3.14;
let notANumber: number = NaN;
let infinito: number = Infinity;

// BigInt (tipo separado, não number)
let big: bigint = 9007199254740991n;

boolean

O tipo boolean aceita apenas true ou false.

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

// Uso comum em condicionais
if (ativo) {
    console.log("Usuário ativo");
}

Exemplos práticos combinando inferência e anotação:

// Inferência
let produto = "Notebook"; // TypeScript infere string
let preco = 2500.50;      // infere number
let disponivel = true;    // infere boolean

// Anotação explícita (útil quando a declaração está separada da atribuição)
let nomeCliente: string;
let idadeCliente: number;
let cadastroCompleto: boolean;

nomeCliente = "Ana";
idadeCliente = 28;
cadastroCompleto = true;

3. any: o tipo desligado

O tipo any é a "válvula de escape" do sistema de tipos. Quando você declara uma variável como any, o TypeScript desativa completamente a checagem de tipos para aquela variável.

let dado: any = "texto";
dado = 42;        // OK
dado = true;      // OK
dado.metodoInexistente(); // Sem erro de compilação, mas pode quebrar em runtime

Cenários de uso legítimos:
- Migração gradual de JavaScript para TypeScript
- Dados vindos de APIs externas sem tipagem definida
- Interação com bibliotecas JavaScript que não possuem tipos

Riscos: O any elimina a segurança que o TypeScript oferece. Use com moderação e prefira unknown quando possível.

// Exemplo de migração: código JS antigo sendo convertido
let dadosUsuario: any = fetchUserFromLegacyAPI();

4. unknown: o tipo seguro para dados desconhecidos

O tipo unknown é semanticamente similar ao any, mas com uma diferença crucial: você não pode operar com valores unknown sem primeiro fazer uma verificação de tipo (narrowing).

let valorDesconhecido: unknown = "Olá";

// Erro: não é possível acessar propriedades sem verificação
// console.log(valorDesconhecido.length); // Erro de compilação

// Narrowing obrigatório
if (typeof valorDesconhecido === "string") {
    console.log(valorDesconhecido.length); // Agora OK
}

Exemplo prático: parsing de API externa

function processarRespostaAPI(resposta: unknown): string {
    // Narrowing passo a passo
    if (typeof resposta !== "object" || resposta === null) {
        throw new Error("Resposta inválida");
    }

    // Precisamos de um tipo mais específico para acessar propriedades
    if ("nome" in resposta && typeof (resposta as any).nome === "string") {
        return (resposta as any).nome;
    }

    return "Desconhecido";
}

5. never: o tipo do impossível

O tipo never representa valores que nunca podem ocorrer. É usado principalmente em funções que nunca retornam (lançam exceção ou entram em loop infinito) e em verificações exaustivas de união de tipos.

// Função que nunca retorna (lança exceção)
function erro(mensagem: string): never {
    throw new Error(mensagem);
}

// Função com loop infinito
function loopInfinito(): never {
    while (true) {
        // processamento contínuo
    }
}

Uso em exhaustive checking com switch:

type Forma = "circulo" | "quadrado" | "triangulo";

function area(forma: Forma): number {
    switch (forma) {
        case "circulo":
            return Math.PI * 5 * 5;
        case "quadrado":
            return 10 * 10;
        case "triangulo":
            return (10 * 8) / 2;
        default:
            // Se adicionarmos um novo tipo em Forma sem tratá-lo aqui,
            // o TypeScript apontará erro
            const _exaustivo: never = forma;
            throw new Error(`Forma não tratada: ${forma}`);
    }
}

6. Comparação prática: quando usar cada tipo

Tipo Quando usar Evitar quando
string/number/boolean Dados conhecidos e fixos Dados de fontes externas não validadas
unknown Dados externos (APIs, input do usuário) Dados internos com tipo conhecido
never Controle de fluxo, verificações exaustivas Valores que realmente existem
any Migração rápida, prototipação Código de produção sem necessidade
// Exemplo completo: processamento seguro de entrada
function processarEntrada(dado: unknown): string | number {
    if (typeof dado === "string") {
        return dado.toUpperCase();
    }
    if (typeof dado === "number") {
        return dado * 2;
    }
    // Se chegar aqui, algo inesperado aconteceu
    return erro("Tipo não suportado");
}

function erro(msg: string): never {
    throw new Error(msg);
}

7. Erros comuns e boas práticas

Erro 1: Confundir null/undefined com never

function retornarNulo(): null {
    return null; // Correto: null é um valor válido
}

// Incorreto: never não pode retornar valor algum
// function retornarNulo(): never {
//     return null; // Erro de compilação
// }

Erro 2: Usar any por preguiça em vez de unknown

// Ruim: perde toda segurança de tipo
function processar(dado: any) {
    return dado.nome.toUpperCase();
}

// Bom: força verificação de tipo
function processarSeguro(dado: unknown) {
    if (typeof dado === "object" && dado !== null && "nome" in dado) {
        return (dado as any).nome.toUpperCase();
    }
    throw new Error("Dado inválido");
}

Erro 3: Esquecer de fazer narrowing em unknown

function imprimir(dado: unknown) {
    // Erro: não podemos chamar toString diretamente
    // console.log(dado.toString()); 

    // Correto: verificar antes
    if (dado !== null && dado !== undefined) {
        console.log(String(dado));
    }
}

Dica importante: Ative strict: true no tsconfig.json para que o TypeScript pegue esses problemas automaticamente.

{
  "compilerOptions": {
    "strict": true
  }
}

Com strict ativado, o TypeScript exige que você trate null e undefined adequadamente, além de forçar verificações mais rigorosas com unknown.

Referências