🗺️ Mapped Types

Les mapped types transforment chaque propriété d'un type existant.

// Rendre toutes les props optionnelles (comme Partial)
type MyPartial<T> = {
  [K in keyof T]?: T[K];
};

// Rendre toutes les props readonly
type Freeze<T> = {
  readonly [K in keyof T]: T[K];
};

// Rendre nullable toutes les props
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

// Remapper les clés avec 'as'
type Getters<T> = {
  [K in keyof T as `get${Capitalize}`]: () => T[K];
};

🎭 Conditional Types

// Syntaxe : T extends U ? X : Y
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>;  // "yes"
type B = IsString<number>;  // "no"

// NonNullable custom
type MyNonNullable<T> = T extends null | undefined ? never : T;

// Extraire le type des éléments d'un tableau
type ElementType<T> = T extends (infer E)[] ? E : never;
type Nums = ElementType<number[]>;  // number

// infer — extraire un type dans une position
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type Value = UnpackPromise<Promise<string>>; // string

🔑 keyof & typeof

// keyof — union de toutes les clés d'un type
interface User { id: number; name: string; email: string; }
type UserKey = keyof User; // "id" | "name" | "email"

// typeof — type d'une valeur runtime
const config = { host: "localhost", port: 3000 };
type Config = typeof config; // { host: string; port: number }

// Combiné — accès sécurisé à une propriété
function pluck<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
  return keys.map(k => obj[k]);
}
pluck(config, ["host", "port"]); // (string | number)[]

📝 Template Literal Types

// Combiner des types littéraux comme des template strings
type Orientation = "horizontal" | "vertical";
type Alignment   = "start" | "center" | "end";
type FlexClass   = `flex-${Orientation}-${Alignment}`;
// "flex-horizontal-start" | "flex-horizontal-center" | ... (6 combos)

// Générer des event handlers
type EventName = "click" | "focus" | "blur";
type EventHandler = `on${Capitalize}`;
// "onClick" | "onFocus" | "onBlur"

// Route typée
type ApiRoute = `/api/${string}`;
const route: ApiRoute = "/api/users"; // OK

🛡️ Type Guards

// typeof guard
function process(val: string | number) {
  if (typeof val === "string") {
    val.toUpperCase(); // TS sait que val est string ici
  } else {
    val.toFixed(2);    // TS sait que val est number ici
  }
}

// instanceof guard
function handleError(e: unknown) {
  if (e instanceof Error) console.log(e.message);
}

// Custom type guard — prédicat de type
function isUser(val: unknown): val is User {
  return typeof val === "object"
    && val !== null
    && "id" in val
    && "name" in val;
}

// Assertion function
function assertString(val: unknown): asserts val is string {
  if (typeof val !== "string") throw new TypeError("Expected string");
}

🧮 Exclude, Extract, NonNullable

type A = string | number | boolean;

// Exclude<T, U> — retire U de T
type B = Exclude<A, boolean>;        // string | number

// Extract<T, U> — garde uniquement U dans T
type C = Extract<A, string | number>; // string | number

// NonNullable — retire null et undefined
type D = NonNullable<string | null | undefined>; // string

// Awaited — déplie les Promises imbriquées
type E = Awaited<Promise<Promise<string>>>; // string
📝 Exercices T05 🎯 QCM T05 ▶ Mini-projet T06 — Configuration →