Todos os posts
Type Systemssexta-feira, 01 de novembro de 2024

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

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 never
type 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 pattern
function 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 widening
const featureFlags = {
checkoutV2: true,
leadScoring: false,
multiTenantBeta: true,
} satisfies Record<string, boolean>;
// checkoutV2 continua inferido como true

5) 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ínio
type 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ósito
type 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 David

Trate 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.

TypeScriptArquiteturaQualidadeDX
Leonardo David

Escrito por Leonardo David

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