TypeScript Avanzato — Cheat Sheet

Tag: typescript, generics, type-system, utility-types, typing

Livello: avanzato

TL;DR: Tecniche avanzate di modellazione statica dei tipi in TypeScript: conditional types, mapped types, template literal types e pattern di narrowing sicuro per API robuste a compile-time.

Concetti chiave

Generics con vincoli (extends) garantiscono contratti precisi tra input e output.

Conditional types (T extends U ? X : Y) permettono decisioni dinamiche sui tipi a tempo di compilazione.

infer estrae variabili di tipo all'interno della clausola di controllo di un conditional type.

Mapped types iterano sulle chiavi di un tipo con la sintassi [K in keyof T].

I modificatori di omogeneità + o - possono aggiungere o rimuovere readonly e ? nei mapped types.

Key remapping con as consente di trasformare o filtrare le chiavi in un mapped type.

Template literal types combinano union di stringhe per creare formati testuali fortemente tipizzati.

Type predicate (val is Type) e assertion function (asserts val is Type) restringono i tipi nei flussi di controllo.

Discriminated union usa una proprietà comune per abilitare narrowing esaustivo e pattern matching.

Brand types simulano nominal typing su tipi strutturali primitivi per impedire assegnazioni errate accidentali.

Domande tipiche e risposte pronte

Q: Come funziona la keyword infer nei conditional types?R: La uso dentro la condizione extends per dichiarare una variabile di tipo che TypeScript deduce automaticamente dal tipo passato, come estrarre il return type di una funzione o il tipo interno di una Promise.

Q: Qual è la differenza tra unknown e any?R: any disabilita completamente il type checking; unknown invece forza il type-narrowing prima di consentire qualsiasi operazione sul valore, rendendo il codice totalmente type-safe.

Q: Come si implementa l'exhaustive checking in uno switch su una discriminated union?R: Nel ramo default assegno il valore a una variabile di tipo never; se in futuro aggiungo una variante senza gestirla, il compilatore solleva un errore statico.

Insidie e errori comuni

I conditional types distribuiscono automaticamente sulle union solo se il tipo controllato è un parametro nudo; per disabilitare la distribuzione si usa [T] extends [U].

L'operatore keyof su una union restituisce l'intersezione delle chiavi comuni, non l'unione di tutte le chiavi disponibili.

I type assertion con as sovrascrivono il compilatore nascondendo bug a runtime; è sempre preferibile usare type guard o schema validator.

L'operatore non-null assertion ! elimina il controllo a compile-time ma espone a crash a runtime se il valore è effettivamente null o undefined.

Snippet e comandiTypeScript

// Estrazione return type con infer
type CustomReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// Mapped type con key remapping e rimozione opzionalità
type ConcreteGetters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]-?: () => T[K];
};

// Nominal typing tramite branding
type Brand<K, T> = K & { readonly __brand: T };
type UserId = Brand<string, "UserId">;
type OrderId = Brand<string, "OrderId">;

// Exhaustive checking con never
type Action = { type: "add"; delta: number } | { type: "reset" };

function reducer(action: Action): void {
  switch (action.type) {
    case "add":
      return;
    case "reset":
      return;
    default: {
      const _exhaustiveCheck: never = action;
      return _exhaustiveCheck;
    }
  }
}

Da ripassare

[ ] Regole di bivarianza e contravarianza nei parametri delle funzioni

[ ] Implementazione manuale di utility types built-in come Omit, Pick e Awaited

[ ] Manipolazione complessa di stringhe con template literal types ricorsivi

[x] Discriminated union e narrowing con switch statement

Jarvis Interviews
Jarvis