Todos os artigos
144 artigos · atualizado semanalmente Veja nossas Ferramentas
Todos os artigos
Todos

TypeScript vale a pena em projetos pequenos?

TS adiciona um build step, mas paga em autocompletar, refactor seguro e bugs pegos cedo. Veja o tradeoff real e quando NAO compensa em projeto pequeno.

TypeScript vale a pena em projetos pequenos?
COVER · Todos

"TypeScript é coisa de projeto grande" é uma das frases mais repetidas e menos verdadeiras do ecossistema JavaScript. A ideia por trás dela é defensável: TS adiciona um build step, exige configuração e, no começo, parece que você está digitando o dobro de código para o mesmo resultado. Em um script de 200 linhas, esse atrito assusta. Mas o cálculo mudou nos últimos anos, e o que era um custo real de setup virou quase ruído. A pergunta certa não é mais "vale a pena para projeto pequeno?", e sim "em que ponto exato o protótipo deixa de ser descartável?".

Este texto é sobre o tradeoff concreto: o que você paga, o que você ganha e quando vale mais a pena ignorar o type system de propósito.

O custo de hoje é menor do que você lembra

A reputação de "fricção" do TypeScript vem de uma era de tsconfig.json gigante, ts-node lento e webpack mal configurado. Esse mundo morreu. Hoje, tsx roda um arquivo .ts direto, sem build separado: npx tsx script.ts e pronto. vite entende TS nativamente, bun executa TS sem nenhuma etapa intermediária, e o node recente já tem strip de tipos embutido. O "build step" que tanto assustava virou, na prática, uma flag que você nem percebe.

Isso muda a conversa. Quando o setup custava uma tarde, fazia sentido pular TS em algo pequeno. Quando custa um npm i -D typescript e um tsconfig de seis linhas, o custo de oportunidade praticamente sumiu. Você paga segundos, não horas.

Onde o benefício aparece — e aparece cedo

O ganho do TypeScript não é proporcional ao tamanho do projeto; ele é proporcional à quantidade de vezes que você lê e altera o mesmo código. E mesmo um script de 200 linhas você reabre na semana seguinte sem lembrar o formato daquele objeto de configuração.

Três benefícios aparecem antes das 200 linhas:

  • Autocompletar de verdade. Com tipos, o editor sabe que user. oferece name e email, não naem. Em JS puro, o autocompletar é um chute baseado em heurística.
  • Refactor seguro. Renomear um campo, mudar a assinatura de uma função, mover um módulo — o compilador aponta cada lugar que quebrou antes de você rodar.
  • Bugs pegos antes de executar. undefined is not a function às 23h vira um sublinhado vermelho às 14h.

Considere o caso mais comum em projeto pequeno: consumir uma API.

type Repo = { name: string; stars: number; archived: boolean };

const res = await fetch("https://api.exemplo.com/repos");
const repos: Repo[] = await res.json();

const ativos = repos.filter((r) => !r.archived);

A partir daqui, r. te entrega name, stars e archived. Errar r.star (sem o "s") vira erro de compilação, não um undefined silencioso somado em um total. Escrever esses tipos à mão é chato, e é exatamente o tipo de tarefa que você não deveria fazer manualmente: jogar uma resposta de exemplo em um gerador de tipos a partir de JSON cospe a interface pronta em segundos, e você só ajusta o que o exemplo não cobriu.

Inferência: você escreve menos do que pensa

A objeção de "digitar o dobro" parte de uma imagem errada do TypeScript, em que cada variável carrega uma anotação explícita. Na prática, a inferência faz o trabalho pesado e você anota pouco.

// Nenhuma anotação aqui — TS infere tudo
const total = [1, 2, 3].reduce((a, b) => a + b, 0); // number
const nomes = ["ana", "bia"].map((n) => n.toUpperCase()); // string[]

A regra prática que funciona: anote os limites (parâmetros de função, retorno de API, formato de configuração) e deixe o interior inferir. Você acaba escrevendo menos anotação do que imagina, e o editor continua sabendo o tipo de tudo. Quem reclama de verbosidade quase sempre está anotando o que o compilador já sabia.

any é dívida, não escape

any é a válvula de escape do TypeScript: ele desliga a checagem para aquele valor. É tentador usá-lo para "fazer o erro sumir", mas todo any é uma dívida — um buraco por onde os bugs que o TS deveria pegar voltam a passar, em silêncio.

function parse(raw: any) {
  return raw.data.items.map((i) => i.id); // zero checagem, zero autocompletar
}

Em projeto pequeno o impacto de any é menor, mas o hábito é o mesmo que apodrece projetos grandes. Quando você não sabe o tipo, unknown é quase sempre a escolha melhor: ele força você a verificar antes de usar, em vez de fingir que sabe.

Quando NÃO compensa

Ser honesto sobre o tradeoff inclui admitir onde TS atrapalha. Não vale a pena:

  • Protótipo descartável de fim de semana, em que o objetivo é validar uma ideia em uma tarde e jogar fora. Brigar com o type system aqui é desperdício — o código não vai sobreviver à segunda-feira.
  • Script de uma vez, rodado uma única vez e deletado: um .js colado no terminal resolve.
  • Momentos de exploração dentro de um projeto tipado, em que você ainda não sabe o formato dos dados. Aqui, any ou // @ts-expect-error temporário são aceitáveis — desde que voltem a ser tipados quando a forma estabilizar.

A linha divisória é simples: o código vai ser lido de novo? Se sim, tipos pagam. Se é genuinamente efêmero, não force.

JSDoc: o meio-termo sem build

Existe uma terceira via que poucos lembram. Você pode ter checagem de tipos em arquivos .js puros, sem nenhum build step, usando JSDoc mais // @ts-check:

// @ts-check

/** @param {string} nome @param {number} idade */
function saudar(nome, idade) {
  return `${nome} tem ${idade} anos`;
}

saudar("Ana", "30"); // erro: '30' não é number

O VS Code lê isso e te dá os mesmos sublinhados vermelhos do TS, com zero configuração de build. É um excelente meio-termo para um script pequeno em que você quer a rede de segurança sem nem criar um tsconfig. Não escala tão bem para tipos complexos (generics ficam verbosos em JSDoc), mas para o caso pequeno é subestimado.

Minha opinião: na dúvida, use TS

Depois de migrar mais código JS legado do que gostaria, minha posição é direta: na dúvida, comece tipado. O motivo é menos técnico e mais sobre como projetos se comportam. O "projeto pequeno" quase sempre cresce — aquele script que era para rodar uma vez vira um cron, depois ganha um endpoint, depois vira a base de um produto. E migrar JavaScript legado para TypeScript depois é genuinamente pior do que ter começado tipado: você herda decisões implícitas, formatos de dados não documentados e centenas de any que precisam ser desfeitos um por um.

Começar tipado custa minutos. Migrar depois custa dias. A assimetria é grande demais para apostar contra.

A única exceção honesta é o protótipo de fim de semana de verdade. Ali, não brigue com o type system — se um any destrava você, use e siga em frente. O objetivo é aprender se a ideia presta, não construir um monumento. Para esse caso, e só para esse, o JS solto ainda é a ferramenta certa. Se você está chegando agora e quer firmar a base antes de adicionar tipos, vale revisar o que é JavaScript primeiro — TypeScript faz muito mais sentido quando o JS embaixo já é familiar.

Perguntas frequentes

TypeScript deixa o código mais lento?

Não em runtime. Os tipos são apagados na compilação — o JavaScript que roda é o mesmo que você escreveria à mão, sem nenhuma checagem em tempo de execução. O único custo é no build/dev, e ferramentas como tsx, vite e bun tornaram esse custo quase imperceptível em projetos pequenos.

Preciso configurar um tsconfig complicado?

Não. Um tsconfig.json mínimo com "strict": true e o target já resolve a maioria dos casos pequenos. A maior parte da complexidade de configuração vem de monorepos, paths customizados e integrações com bundlers — nada disso é necessário num script de algumas centenas de linhas.

Vale converter um projeto JS pequeno que já existe?

Geralmente sim, se ele vai continuar vivo. Você pode migrar incrementalmente: renomeie .js para .ts um arquivo por vez, tolere any temporário e aperte os tipos aos poucos. Se o projeto está congelado e ninguém vai mexer, deixe como está — migrar código morto não paga.

TypeScript ou JSDoc para um script pequeno?

JSDoc com // @ts-check é ótimo se você quer checagem sem nenhum build step e os tipos são simples. Assim que aparecerem generics, unions complexas ou muitos tipos compartilhados, TS puro fica mais limpo e menos verboso. Comece com JSDoc se a aversão a build for o bloqueio; mude para TS quando a verbosidade incomodar.

O que levar daqui

O custo do TypeScript em projeto pequeno encolheu a ponto de não ser mais um argumento sério: o build step virou uma flag, e o benefício de autocompletar, refactor seguro e bugs pegos cedo aparece bem antes das 200 linhas. Reserve o JavaScript solto para o que é genuinamente descartável — protótipos de fim de semana e scripts de uma vez. Para todo o resto, comece tipado: é mais barato pagar minutos agora do que dias de migração depois, quando o "projeto pequeno" inevitavelmente crescer.

RD
Autor
Rafael Duarte
Desenvolvedor backend com passagem por fintech e SaaS B2B — trabalhou em times que escalaram APIs de zero a milhões de requisições. Carrega cicatrizes de produção suficientes para ter opiniões fortes sobre ferramentas, padrões e decisões de arquitetura. Não é acadêmico: leu a RFC do UUID quando precisou escolher entre v4 e v7 para uma tabela de alta escrita.
Ver perfil