Tips & Tricks (Mis à jour: 03/06/2026)

Claude Code et TypeScript : aller vite sans perdre la sécurité

Renforcez TypeScript avec Claude Code grâce à strict, Zod, Union, génériques, satisfies et tests de types.

Claude Code et TypeScript : aller vite sans perdre la sécurité

Claude Code accélère fortement le développement TypeScript, surtout pour les formulaires, les helpers d’API et les tests. Mais si les frontières de types sont floues, il peut produire très vite du code fragile. Pour débuter sans se piéger, fixez les règles de types avant de demander la fonctionnalité.

Ici, strict signifie que TypeScript refuse davantage de code douteux. Les types métier écrivent les règles produit sous forme de types. Une Union discriminée modélise des états avec des formes différentes, et la validation runtime vérifie les données pendant l’exécution.

Donner d’abord une carte de types

Avant un long prompt, fournissez une petite carte : règles du compilateur, types métier, entrées externes, états et tests de types. Cette carte rend la sortie de Claude Code plus simple à relire.

flowchart TD
  A["Besoin"] --> B["tsconfig: règles strict"]
  B --> C["Types métier: Plan et Account"]
  C --> D["Données externes: unknown puis validation"]
  D --> E["État: Union discriminées"]
  E --> F["Tests de types: expectTypeOf / tsd"]
  F --> G["Implémentation et revue avec Claude Code"]

Les références officielles à garder ouvertes sont strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, Narrowing, Generics, Utility Types et la note sur satisfies. Pour la validation runtime, ajoutez la documentation Zod.

À lire aussi : TypeScript Utility Types, TypeScript Generics et Zod Validation.

Commencer par un tsconfig strict

Ne dites pas seulement “fais-le en TypeScript”. Donnez d’abord le contrat du compilateur.

{
  "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"]
}

Ajoutez ces contraintes au prompt.

Ce dépôt utilise TypeScript en mode strict.
N'introduis pas any. Les entrées externes doivent être unknown puis validées avec Zod.
Pour les Union dans les switch, ajoute un contrôle d'exhaustivité avec never.
Après l'implémentation, exécute npx tsc --noEmit.

noUncheckedIndexedAccess garde la possibilité de undefined quand on lit un tableau ou un record. C’est strict, mais cela révèle tôt les champs API manquants, les listes vides et les traductions incomplètes.

Cas 1 : modéliser les offres SaaS

Les types métier traduisent les règles produit en TypeScript. Offres, permissions, factures et états de publication doivent être modélisés avant l’interface.

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 retire des membres d’une Union. Partial rend les propriétés optionnelles : utile pour une mise à jour, risqué pour une création si on l’applique trop largement.

Cas 2 : valider les données API depuis unknown

Les types TypeScript disparaissent à l’exécution. API, formulaires, cookies, localStorage, CSV et sorties d’IA peuvent être invalides. Recevez-les en unknown, validez-les, puis utilisez le résultat typé.

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 signifie que la valeur n’est pas encore prouvée. Contrairement à any, il impose une validation avant l’accès aux propriétés.

Cas 3 : fermer l’état de paiement avec une Union

Paiements, uploads, formulaires et jobs sont des machines à états. Évitez status: string, trop large.

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;
    }
  }
}

La branche never oblige TypeScript à vérifier que tous les cas valides sont traités. Si l’état refunded arrive plus tard, la branche manquante devient visible.

Cas 4 : génériques et satisfies

Les génériques rendent un helper réutilisable tout en gardant les types précis.

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));

Pour les objets de configuration, satisfies est souvent plus sûr qu’une assertion large.

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];
}

Ajouter des tests de types

Les types publics importants méritent des tests.

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;
  }>();
});

Cas pratiques et pièges (Use case / Pitfall checklist)

Cas d’usageCe que les types verrouillentCe que Claude Code génère
Facturation SaaSoffres, factures, permissionsbranches UI, formulaires, messages
Admin APIschémas Zod, réponsesfetch, tableaux, chargement
CMS d’articlesslug, langue, publication, imagebrouillons MDX, listes, corrections
Formulaire contactschema d’entrée, Union résultatUI, envoi, tests Vitest
PiègeEffetCorrection
Réponse API en anyJSON invalide acceptéunknown et Zod
status: stringétats impossiblesUnion discriminée
Beaucoup de as Usererreurs masquéesschema, gardes, satisfies
Partial<T> pour créerchamps requis devenus optionnelsséparer create et update
Pas de tests de typesAPI de types élargieexpectTypeOf ou tsd

Dans les workflows ClaudeCodeLab, Masa a déjà vu des URL de locale invalides parce que lang était un simple string. Une Union fermée pour les locales a rendu les corrections de Claude Code beaucoup plus prévisibles.

Règles et 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.

Pour un projet personnel, le catalogue de produits réunit des templates et checklists pour Claude Code. Pour une équipe, la formation et le conseil Claude Code peuvent couvrir migration strict, frontières Zod, tests de types et règles CLAUDE.md dans un vrai dépôt.

Résultat testé

J’ai testé ce flux sur un petit projet TypeScript : remplacer les réponses API en any par unknown plus Zod, puis demander à Claude Code d’ajouter les branches Union et les tests expectTypeOf. Le gain concret a été de trouver des états non traités et des accès à des propriétés inexistantes avant la revue.

#Claude Code #TypeScript #type safety #development efficiency #frontend
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.