Boas Práticas de TypeScript para 2024
Padrões de TypeScript para produção em 2024: strict real, modelagem de domínio com unions, boundaries com unknown, satisfies para inferência precisa e controle de complexidade de tipos.
Leonardo David
10 min de leitura
TypeScript entrega alavancagem real quando os tipos modelam decisões de domínio e riscos do sistema. Usá-lo apenas como lint avançado desperdiça sua capacidade de reduzir incerteza arquitetural.
Em 2024, TypeScript está consolidado como base estrutural do ecossistema frontend e backend JavaScript. A diferença entre código tipado e código confiável está na disciplina: modelagem explícita, fronteiras bem definidas e inferência preservada.
1) Strict não é opcional
Ative strict, noUncheckedIndexedAccess e exactOptionalPropertyTypes quando possível. Configuração permissiva transfere risco para runtime e mascara erros estruturais.
// tsconfig mínimo para produção{ "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, "noImplicitOverride": true, "useUnknownInCatchVariables": true } }2) Modele fluxos com unions discriminadas
Estados implícitos geram bugs. Estados explícitos geram previsibilidade. Unions discriminadas tornam fluxos exaustivos e impedem combinações inválidas.
// tratamento exaustivo com nevertype JobState = | { status: "queued" } | { status: "running"; startedAt: Date } | { status: "failed"; reason: string } | { status: "done"; resultId: string }; function renderJobState(state: JobState) { switch (state.status) { case "queued": return "Waiting"; case "running": return "Processing"; case "failed": return state.reason; case "done": return state.resultId; default: { const _exhaustive: never = state; return _exhaustive; } } }3) Unknown nas fronteiras externas
Qualquer dado vindo de API, banco, localStorage ou input do usuário deve entrar no sistema como unknown. A validação é parte da arquitetura, não detalhe opcional.
// boundary patternfunction parseUser(input: unknown): User { if ( typeof input === "object" && input !== null && "id" in input && "email" in input ) { return input as User; } throw new Error("Invalid user payload"); }4) Preserve inferência com satisfies
Evite anotar objetos quando isso destrói inferência literal. satisfies valida a forma sem ampliar tipos desnecessariamente.
// configuração segura sem wideningconst featureFlags = { checkoutV2: true, leadScoring: false, multiTenantBeta: true, } satisfies Record<string, boolean>; // checkoutV2 continua inferido como true5) Tipos modelam domínio, não estrutura técnica
Evite 'DTO genérico'. Modele conceitos reais: OrderId, EmailAddress, Money, TenantId. Isso reduz mistura acidental de valores semanticamente diferentes.
// branded types para domíniotype Brand<K, T> = K & { __brand: T }; type OrderId = Brand<string, "OrderId">; type TenantId = Brand<string, "TenantId">; function getOrder(id: OrderId) {}6) Prefira composição de tipos a herança estrutural excessiva
Interfaces gigantes com múltiplas responsabilidades tornam o sistema frágil. Prefira compor tipos menores e explícitos.
7) Evite any. Se precisar, documente o risco
any remove garantias. Se for inevitável (interop legado), isole em boundary explícita e converta rapidamente para tipos seguros.
8) Controle complexidade de generics
Tipos extremamente complexos degradam DX e performance do compilador. Se um tipo exige comentário extenso para ser entendido, reavalie a modelagem.
9) Use utility types de forma intencional
// Partial, Pick e Omit com propósitotype UpdateUserInput = Partial< Pick<User, "name" | "email" | "avatar"> >;10) Faça tipos refletirem invariantes reais
Se algo pode ser undefined, modele explicitamente. Se não pode, não use optional por conveniência. Tipos frouxos criam validações invisíveis espalhadas pelo código.
“TypeScript bem aplicado reduz incerteza nas bordas do sistema e torna decisões de domínio explícitas no código.”
— Leonardo DavidTrate o sistema de tipos como parte da arquitetura. Ele deve proteger fronteiras, modelar estados impossíveis e deixar regras do domínio claras. Em 2024, essa disciplina é diferencial estrutural em qualquer codebase JavaScript.

Escrito por Leonardo David
Engenheiro Full Stack. Atuo construindo produtos de alta performance na intersecção entre frontend, backend e estratégia de produto.