💻 Développement

dev-typescript-mastery

Maîtrise de TypeScript avec types avancés et patterns.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- dev-typescript-mastery --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } dev-typescript-mastery -Launch"

🚀 Déjà installé ?

claude "/dev-typescript-mastery"

Ou tapez /dev-typescript-mastery dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

TypeScriptTStypesgenericsinterfacetype guardutility typesstrict modetsconfig

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/dev-typescript-mastery ~/.claude/skills/

Payload du plugin : skills/dev-typescript-mastery · source éditable : dev-skills/typescript-mastery

📖 Manuel

TypeScript Mastery

1. Configuration — point de départ obligatoire

tsconfig.json minimal recommandé (2026) :

{
  "compilerOptions": {
    "strict": true,
    "target": "ES2022",
    "moduleResolution": "bundler",   // Vite/esbuild/webpack5
    // "moduleResolution": "node16", // Node pur (CJS/ESM hybride)
    "verbatimModuleSyntax": true,    // clarifie import type vs import
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true,
    "paths": { "@/*": ["./src/*"] }
  }
}

Critères de choix moduleResolution :

ContexteValeur
Vite, esbuild, webpack 5bundler
Node 18+ natif (ESM)node16 ou nodenext
Lib publiée NPMnode16
Legacy CJSnode

Monorepo : utiliser references + composite: true par package pour éviter que tsc recompile tout.


2. Types fondamentaux — choisir la bonne construction

// Union + literal types — préférer aux enums
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";

// Branded types — évite les confusions de primitives
type UserId = string & { readonly _brand: "UserId" };
type OrderId = string & { readonly _brand: "OrderId" };
const toUserId = (s: string): UserId => s as UserId; // cast unique et localisé

// Tuple typé
type Pair<A, B> = [A, B];
const pair: Pair<string, number> = ["age", 30];

// Discriminated union — exhaustivité vérifiée par TS
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rect"; w: number; h: number };

function area(s: Shape): number {
  switch (s.kind) {
    case "circle": return Math.PI * s.radius ** 2;
    case "rect":   return s.w * s.h;
    // TS erreur si un cas manque (avec noImplicitReturns)
  }
}

type vs interface :


3. Generics avancés

// Constraint + conditional type + infer
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;
// UnwrapPromise<Promise<User>> => User

// Mapped type : rendre tous les champs optionnels et readonly
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

// Template literal type
type EventName<T extends string> = `on${Capitalize<T>}`;
// EventName<"click"> => "onClick"

// NoUndefined sur les clés optionnelles
type RequiredFields<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;

4. Utility types — référence rapide

Built-inUsage concret
Partial<T>Formulaire de mise à jour partielle
Required<T>Forcer tous les champs après validation
Pick<T, K>Projeter une sous-forme
Omit<T, K>Exclure password d'un DTO user
Record<K, V>Map statique clé-valeur
Readonly<T>Config immuable
ReturnType<F>Typer le retour d'une fonction inconnue
Awaited<T>Déballer le type d'une Promise
Parameters<F>Forwarding d'arguments
NonNullable<T>Après un guard null
// Utility custom DeepPartial
type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] };

5. Type guards et narrowing

// User-defined type guard
function isUser(x: unknown): x is User {
  return typeof x === "object" && x !== null && "id" in x && "email" in x;
}

// Assertion function (throw si faux)
function assertDefined<T>(val: T | undefined, msg: string): asserts val is T {
  if (val === undefined) throw new Error(msg);
}

// Exhaustive check avec never
function assertNever(x: never): never {
  throw new Error(`Unhandled case: ${JSON.stringify(x)}`);
}
// Placer en default d'un switch sur discriminated union

6. Patterns métier

Result type (gestion d'erreurs sans exception) :

type Result<T, E = Error> =
  | { ok: true;  value: T }
  | { ok: false; error: E };

async function fetchUser(id: UserId): Promise<Result<User>> {
  try {
    const data = await api.get(`/users/${id}`);
    return { ok: true, value: data };
  } catch (e) {
    return { ok: false, error: e as Error };
  }
}
// Consommateur forcé de gérer les deux cas

Validation runtime → types TS avec Zod :

import { z } from "zod";

const UserSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  role: z.enum(["admin", "user"]),
});
type User = z.infer<typeof UserSchema>; // source de vérité unique

const user = UserSchema.parse(rawJson); // throw si invalide
const safe  = UserSchema.safeParse(rawJson); // safe.success / safe.data

Repository générique typé :

interface Repository<T, Id> {
  findById(id: Id): Promise<T | null>;
  save(entity: T): Promise<T>;
  delete(id: Id): Promise<void>;
}
class UserRepo implements Repository<User, UserId> { ... }

7. APIs type-safe

// Génération depuis OpenAPI (zero runtime overhead)
// npx openapi-typescript openapi.yaml -o src/api.d.ts

import type { paths } from "./api";
import createClient from "openapi-fetch";

const client = createClient<paths>({ baseUrl: "/api" });
const { data, error } = await client.GET("/users/{id}", {
  params: { path: { id: "123" } },
});
// data est typé automatiquement selon le schéma OpenAPI

8. Debugging des types

// "Aplatir" un type complexe pour le lire dans l'IDE
type Prettify<T> = { [K in keyof T]: T[K] } & {};

// satisfies — valide sans widening (TS 4.9+)
const config = {
  port: 3000,
  host: "localhost",
} satisfies Record<string, string | number>;
// config.port reste number (pas string | number)

// Tester les types à la compilation
import { expectType, expectError } from "tsd";
expectType<User>(parseUser(raw));
expectError(parseUser(123));

// @ts-expect-error — documenter une erreur intentionnelle
// @ts-expect-error — version legacy de l'API
legacyCall();

9. Anti-patterns et pièges

Anti-patternProblèmeRemède
any partoutDésactive le type checkerunknown + narrowing
as Type sans validationCast dangereux silencieuxZod parse ou type guard
! non-null assertionException runtime non détectéeassertDefined() ou ?.
enum standardGénère du JS runtime, tree-shaking difficileconst enum ou union de literals
Types écrits manuellement dupliquant le schéma DB/APIDésynchronisation garantieGénérer depuis OpenAPI / Prisma / Zod
noUncheckedIndexedAccess: falsearr[0] peut être undefined sans avertissementActiver et gérer le `T \undefined`
Ignorer exactOptionalPropertyTypes{ a?: string } accepte { a: undefined }Activer pour distinguer absent vs undefined
Object ou {} comme type génériqueTrop permissif, accepte toutobject, Record<string, unknown>

10. Bonnes pratiques 2026