Claude Code e TypeScript: dicas práticas para ganhar velocidade com segurança
Use strict, Zod, Union, genéricos, satisfies e testes de tipos para melhorar TypeScript com Claude Code.
Claude Code pode acelerar bastante o desenvolvimento TypeScript, principalmente em formulários, helpers de API e testes. Mas, se as fronteiras de tipo forem vagas, ele também acelera bugs. Para iniciantes, o hábito mais importante é definir as regras de tipo antes de pedir a funcionalidade.
Aqui, strict significa que TypeScript rejeita código suspeito.
Tipos de domínio são regras de negócio escritas como tipos.
Union discriminada modela estados com formatos diferentes, e validação em runtime confere os dados enquanto o programa roda.
Dê um mapa de tipos primeiro
Antes de um prompt longo, prepare um mapa pequeno: regras do compilador, tipos de domínio, entradas externas, estados e testes de tipo. Esse mapa facilita revisar o diff criado por Claude Code.
flowchart TD
A["Requisito"] --> B["tsconfig: regras strict"]
B --> C["Tipos de domínio: Plan e Account"]
C --> D["Dados externos: unknown e validação"]
D --> E["Estado: Union discriminadas"]
E --> F["Testes de tipo: expectTypeOf / tsd"]
F --> G["Implementação e revisão com Claude Code"]
Use a documentação oficial como base: strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, Narrowing, Generics, Utility Types e a nota sobre satisfies.
Para validação em runtime, veja também a documentação do Zod.
Leituras relacionadas: TypeScript Utility Types, TypeScript Generics e Zod Validation.
Comece com tsconfig strict
Não peça apenas “faça em TypeScript”. Fixe primeiro o contrato do compilador.
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts"]
}
Inclua as restrições no prompt.
Este repositório usa TypeScript strict.
Não introduza any. Trate entradas externas como unknown e valide com Zod.
Ao lidar com Union em switch, adicione checagem exaustiva com never.
Depois da implementação, rode npx tsc --noEmit.
noUncheckedIndexedAccess mantém undefined visível ao ler arrays e objetos.
É mais rigoroso, mas encontra campos ausentes de API, listas vazias e traduções incompletas mais cedo.
Caso 1: modelar planos SaaS
Tipos de domínio colocam regras de negócio no TypeScript. Planos, permissões, estado de cobrança e publicação devem vir antes da UI.
export type Plan = "free" | "pro" | "enterprise";
export type Account = {
id: string;
email: string;
plan: Plan;
seats: number;
trialEndsAt: string | null;
};
export type CreateAccountInput = {
email: string;
plan: Exclude<Plan, "enterprise">;
seats?: number;
};
export type UpdateAccountInput = Partial<
Pick<Account, "email" | "plan" | "seats" | "trialEndsAt">
>;
Exclude remove membros de uma Union.
Partial torna propriedades opcionais. É bom para updates, mas perigoso para create input se relaxar campos obrigatórios.
Caso 2: validar API a partir de unknown
Tipos TypeScript somem em runtime.
APIs, formulários, cookies, localStorage, CSV e saídas de IA podem vir quebrados.
Receba como unknown, valide e só depois use o valor tipado.
npm install zod
import { z } from "zod";
const AccountSchema = z.object({
id: z.string().min(1),
email: z.string().email(),
plan: z.enum(["free", "pro", "enterprise"]),
seats: z.number().int().positive(),
trialEndsAt: z.string().datetime().nullable()
});
type Account = z.infer<typeof AccountSchema>;
export function parseAccountResponse(json: unknown): Account {
return AccountSchema.parse(json);
}
unknown significa que o valor ainda não foi provado.
Diferente de any, ele força validação antes de acessar propriedades.
Caso 3: fechar estados de pagamento com Union
Pagamentos, uploads, formulários e jobs são máquinas de estado.
status: string é amplo demais.
type PaymentResult =
| { status: "pending"; invoiceId: string }
| { status: "paid"; invoiceId: string; paidAt: string }
| { status: "failed"; invoiceId: string; reason: string };
export function renderPaymentMessage(result: PaymentResult): string {
switch (result.status) {
case "pending":
return `Invoice ${result.invoiceId} is waiting for payment.`;
case "paid":
return `Invoice ${result.invoiceId} was paid at ${result.paidAt}.`;
case "failed":
return `Invoice ${result.invoiceId} failed: ${result.reason}.`;
default: {
const exhaustive: never = result;
return exhaustive;
}
}
}
O ramo never ajuda TypeScript a exigir todos os casos válidos.
Se refunded for adicionado depois, o compilador cobra o novo branch.
Caso 4: genéricos e satisfies
Genéricos criam helpers reutilizáveis sem perder o tipo específico da chamada.
export function groupBy<T, K extends PropertyKey>(
items: readonly T[],
getKey: (item: T) => K
): Partial<Record<K, T[]>> {
const grouped: Partial<Record<K, T[]>> = {};
for (const item of items) {
const key = getKey(item);
const bucket = grouped[key] ?? [];
bucket.push(item);
grouped[key] = bucket;
}
return grouped;
}
const accounts = [
{ id: "a1", plan: "free" },
{ id: "a2", plan: "pro" },
{ id: "a3", plan: "pro" }
] as const;
const byPlan = groupBy(accounts, (account) => account.plan);
const proAccounts = byPlan.pro ?? [];
console.log(proAccounts.map((account) => account.id));
Em objetos de configuração, prefira satisfies a uma asserção ampla.
type ApiRoute = {
method: "GET" | "POST" | "PATCH" | "DELETE";
path: `/${string}`;
auth: boolean;
};
const routes = {
listAccounts: { method: "GET", path: "/accounts", auth: true },
createAccount: { method: "POST", path: "/accounts", auth: true },
healthCheck: { method: "GET", path: "/health", auth: false }
} as const satisfies Record<string, ApiRoute>;
type RouteName = keyof typeof routes;
export function getRoute(name: RouteName) {
return routes[name];
}
Adicione testes de tipo
Tipos públicos importantes também precisam de teste.
npm install -D vitest tsd
import { expectTypeOf, test } from "vitest";
type CreateAccountInput = {
email: string;
plan: "free" | "pro";
seats?: number;
};
test("CreateAccountInput keeps the public API narrow", () => {
expectTypeOf<CreateAccountInput>().toMatchTypeOf<{
email: string;
plan: "free" | "pro";
seats?: number;
}>();
});
Usos reais e armadilhas (Use case / Pitfall checklist)
| Caso de uso | O que os tipos fixam | O que Claude Code gera |
|---|---|---|
| Cobrança SaaS | planos, faturas, permissões | UI, formulários, mensagens |
| Admin com API | schemas Zod, respostas | fetch, tabelas, loading |
| CMS de artigos | slug, idioma, publicação, imagem | rascunhos MDX, listagens, validação |
| Formulário contato | schema de entrada, Union de resultado | UI, envio, testes |
| Armadilha | Efeito | Correção |
|---|---|---|
Resposta API como any | JSON quebrado compila | unknown e Zod |
status: string | estados impossíveis | Union discriminada |
Muitos as User | erros escondidos | schema, guards, satisfies |
Partial<T> no create | campos obrigatórios viram opcionais | separar create e update |
| Sem testes de tipo | tipos públicos alargam | expectTypeOf ou tsd |
No fluxo de artigos da ClaudeCodeLab, Masa já viu URLs de locale inválido porque lang era apenas string.
Ao fechar o locale em uma Union, as sugestões de Claude Code ficaram bem mais previsíveis.
Regras e CTA
## TypeScript rules
- Use strict TypeScript.
- Do not introduce `any`. Use `unknown` at external boundaries.
- Prefer discriminated unions for states.
- Prefer `satisfies` over broad type assertions.
- Derive API types from Zod schemas when runtime data is involved.
- Add Vitest or tsd style type checks for exported helper types.
- Run `npx tsc --noEmit` before reporting completion.
Para projetos solo, o catálogo de produtos reúne templates e checklists para Claude Code.
Para equipes, o treinamento e consultoria Claude Code pode cobrir migração strict, fronteiras Zod, testes de tipo e regras de CLAUDE.md em um repositório real.
Resultado testado
Testei este fluxo em um projeto TypeScript pequeno: troquei respostas API de any para unknown com Zod e pedi ao Claude Code que adicionasse branches Union e testes expectTypeOf.
O ganho concreto foi encontrar estados não tratados e acessos a propriedades inexistentes antes da revisão.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Escada de segurança de permissões no Claude Code
Amplie de read-only para edições limitadas, comandos de prova e deploy checks sem perder controle.
Claude Code Small PR Proof Pack: pequenas mudanças fáceis de revisar
Um pacote de prova para PRs do Claude Code: diff, checks, URL pública, CTA e rollback.
Gate de revisão antes do commit com Claude Code
Revisão antes do commit com Claude Code: diff, build, URL pública, Gumroad, consultoria, testes e arquivos fora do escopo.