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 é 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.oferecenameeemail, nãonaem. 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
.jscolado no terminal resolve. - Momentos de exploração dentro de um projeto tipado, em que você ainda não sabe o formato dos dados. Aqui,
anyou// @ts-expect-errortemporá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.
- 01 Salt, pepper, bcrypt e Argon2id: como proteger senhas de verdade Em 2012, o LinkedIn expôs 117mi de senhas. SHA-1 sem salt — 90% quebradas em 4h. Entenda o que cada camada de proteção resolve e por que Argon2id é a escolha certa hoje.
- 02 Licenças MIT, Apache e GPL: diferenças práticas para devs MIT, Apache 2.0 e GPL não são a mesma coisa. Entenda permissiva vs copyleft, proteção de patentes e compatibilidade antes de colocar código em produção.