Tipos básicos em TypeScript: string, number e mais
Aprenda inferência, anotações, arrays, objetos, unions, literals e unknown com exemplos executáveis e erros que o compilador encontra antes do Node.
Os tipos básicos do TypeScript descrevem os valores que seu JavaScript já usa:
texto é string, número é number, verdadeiro ou falso é boolean, listas são
arrays e objetos têm propriedades com tipos próprios. A partir dessas peças,
você combina alternativas com unions e protege dados desconhecidos com
unknown.
Nesta lição, a gente vai montar o cadastro de um produto por partes. Você verá o que o compilador infere sozinho, onde uma anotação acrescenta informação e como um erro muda quando o contrato fica mais preciso. Se ainda não tem um projeto rodando, faça antes a instalação do TypeScript.
Etiquetas de estoque: o modelo mental dos tipos
Imagine um estoque em que cada prateleira tem uma etiqueta: “cabos”, “teclados”, “itens frágeis”. A etiqueta não cria o produto nem muda o que existe na caixa. Ela registra que tipo de item pode ocupar aquele lugar e permite perceber um engano antes de o pedido ser separado.
No TypeScript, o valor é o produto, o tipo é a etiqueta e o compilador confere a
compatibilidade. Uma anotação como preco: number não converte texto; ela declara
que apenas números cabem ali. O limite da analogia é que um valor pode satisfazer
várias descrições estruturais ao mesmo tempo, e o compilador consegue deduzir
etiquetas sem você escrevê-las.
O comportamento técnico volta ao centro: tipos existem para análise e são
apagados antes da execução. Quem roda dist/index.js é o Node, usando valores de
JavaScript, não etiquetas do TypeScript.
Deixe a inferência ler o valor evidente
Inferência de tipos é a capacidade de deduzir o tipo pelo valor e pelo contexto. Passe o cursor sobre estas constantes no editor:
const nome = 'Mouse vertical';
const preco = 189.9;
const disponivel = true;
console.log(typeof nome, typeof preco, typeof disponivel);TypeScript infere nome como o literal 'Mouse vertical', preco como o literal
189.9 e disponivel como true, porque const não será reatribuído. No uso
cotidiano, esses valores continuam compatíveis com string, number e
boolean. Escrever as três anotações repetiria o que já está claro.
Com let, o compilador normalmente amplia o tipo para permitir outra atribuição
da mesma família:
let estoque = 10;
estoque = 8;
console.log(estoque);Troque a segunda linha por estoque = 'oito' e rode npx tsc --noEmit:
let estoque = 10;
estoque = 'oito';O compilador inferiu number na primeira atribuição e manteve esse contrato nas
seguintes. O erro não depende de uma anotação explícita; inferência também gera
uma regra verificável.
Anote quando o começo não conta a história inteira
Uma variável que começa vazia e recebe um valor depois precisa de contexto. Sem
ele, let codigo; tende a perder precisão. Declare o conjunto permitido:
let codigoSelecionado: string | null = null;
codigoSelecionado = 'PROD-42';
console.log(codigoSelecionado);O sinal | cria uma union type, traduzida como “um tipo ou outro”. A variável
aceita ausência intencional (null) e depois um texto. Ela não aceita um número.
Esse é um caso em que a anotação acrescenta informação que o valor inicial não
consegue contar.
Parâmetros de função também precisam de anotação no modo estrito, porque o compilador não tem uma chamada única para deduzir a intenção:
function aplicarDesconto(preco: number, percentual: number): number {
return preco - preco * percentual;
}
console.log(aplicarDesconto(200, 0.15).toFixed(2));: number depois dos parênteses descreve o retorno. Se uma manutenção trocar o
retorno por `R$ ${preco}`, o erro aparece dentro da função, perto da causa,
e não apenas em cada lugar que esperava fazer uma conta.
Arrays dizem qual item pode entrar
string[] é um array de strings; Array<string> é uma escrita equivalente.
Escolha uma convenção e mantenha consistência. Aqui o carrinho guarda objetos:
type Item = {
nome: string;
preco: number;
};
const itens: Item[] = [
{ nome: 'Mouse', preco: 189.9 },
{ nome: 'Teclado', preco: 250 },
];
console.log(itens.map((item) => item.nome).join(', '));O tipo se propaga para o callback de map: item.nome oferece operações de
string e item.preco oferece operações de número. Tente inserir { nome: 'Monitor', preco: '900' }; o compilador aponta a propriedade incompatível antes
de o valor contaminar a soma.
Com noUncheckedIndexedAccess, ler itens[0] devolve Item | undefined, porque
o array pode estar vazio. Isso não é burocracia: é a diferença entre uma posição
possível e um item garantido. Você pode usar optional chaining ou testar a
existência antes de acessar.
Objetos descrevem propriedades obrigatórias e opcionais
Uma propriedade com ? é opcional. Ao ler descricao, o tipo será string | undefined, porque a chave pode faltar:
type Produto = {
id: number;
nome: string;
descricao?: string;
};
const produto: Produto = {
id: 42,
nome: 'Mouse vertical',
};
console.log(produto.descricao?.toUpperCase() ?? 'SEM DESCRIÇÃO');?. interrompe o acesso se o lado esquerdo for null ou undefined; ??
fornece o texto alternativo. Essa combinação é JavaScript em execução, enquanto
o tipo opcional fez o editor exigir o cuidado. Se quiser aprofundar a operação,
há uma lição de optional chaining.
Não marque tudo como opcional para “parar de dar erro”. Se todo produto precisa
de nome para aparecer no catálogo, nome: string deve continuar obrigatório.
Um contrato útil diferencia ausência permitida de dado esquecido.
Literal types transformam texto solto em estados conhecidos
O tipo string aceita infinitos textos. Quando o domínio conhece opções
fechadas, use literal types numa union:
type StatusEstoque = 'disponivel' | 'baixo' | 'esgotado';
function aviso(status: StatusEstoque): string {
if (status === 'esgotado') return 'Repor produto';
if (status === 'baixo') return 'Planejar compra';
return 'Estoque normal';
}
console.log(aviso('baixo'));Uma chamada aviso('acabando') reproduz TS2345, porque esse texto não faz
parte do conjunto. A vantagem não é escrever menos caracteres; é centralizar o
vocabulário válido e ganhar autocomplete. Se uma quarta situação entrar no
negócio, você a adiciona ao tipo e revisa os lugares que tomam decisão.
Any desliga a conferência; unknown pede uma prova
any é uma saída de emergência: qualquer valor entra e qualquer operação passa
pela checagem. O problema só volta na execução:
const resposta: any = { total: 300 };
console.log(resposta.tota.toFixed(2));O erro de digitação em tota atravessou o compilador porque any desativou a
análise naquela cadeia. unknown também recebe qualquer valor, mas bloqueia o
uso até existir uma verificação:
function exibirTotal(valor: unknown): string {
if (typeof valor === 'number') {
return valor.toFixed(2);
}
return 'total inválido';
}
console.log(exibirTotal(300));
console.log(exibirTotal('300'));typeof estreitou unknown para number dentro do if. Isso é narrowing,
assunto da lição como estreitar tipos.
Para JSON, formulário e resposta de API, comece com unknown e valide a forma
real. Uma anotação não examina dados externos.
Null e undefined precisam de uma decisão
undefined costuma representar algo que ainda não recebeu valor ou uma
propriedade ausente. null costuma ser uma ausência colocada de propósito. Com
strictNullChecks, nenhum dos dois é aceito automaticamente onde se exige
string.
function saudacao(nome: string | null): string {
return nome === null ? 'Olá, visitante' : `Olá, ${nome}`;
}
console.log(saudacao(null));
console.log(saudacao('Ana'));A checagem nome === null não serve apenas à execução; ela informa ao TypeScript
que o segundo ramo tem uma string. Evite o operador ! para fingir que um valor
existe sem prova. Ele é outra afirmação apagada na compilação e pode apenas mover
o erro para produção.
Missão: descreva um produto sem usar any
Crie um tipo Produto com nome: string, preco: number, ativo: boolean,
tags: string[] e status: 'rascunho' | 'publicado'. Acrescente descricao? e
uma função resumo(produto: Produto): string que devolva nome, preço e status.
type Produto = {
nome: string;
preco: number;
ativo: boolean;
tags: string[];
status: 'rascunho' | 'publicado';
descricao?: string;
};Seu critério de sucesso é observável: um produto válido compila e gera um resumo;
preço textual, status 'pronto' e tag numérica produzem três diagnósticos. Depois
faça uma função receber unknown e devolver o texto somente quando typeof valor === 'string'.
Não avance só porque leu os nomes. Provoque cada erro, localize encontrado e esperado na mensagem e corrija a origem. Na próxima etapa, interface vs type em TypeScript, a gente decide como dar nomes melhores a essas formas sem transformar preferência em regra absoluta.
Perguntas frequentes
TypeScript tem um tipo inteiro diferente de decimal?
Qual a diferença entre any e unknown?
Quando devo anotar o tipo de uma variável?
O que significa o ponto de interrogação numa propriedade?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0 com TypeScript 7.0.2, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- TypeScript Handbook — Everyday Types — typescriptlang.org
- TypeScript Handbook — More on Functions — typescriptlang.org
- TypeScript Handbook — Narrowing — typescriptlang.org


