TypeScript — Cheat Sheet

Tag: typescript, javascript, types, generics, utility-types, frontend, backend

Livello: intermedio

TL;DR: Superset tipizzato di JavaScript con compilazione via type erasure, type inference, strict checking e modellazione avanzata con union, generics e narrowing.

Concetti chiave

Superset sintattico di JavaScript compilato via type erasure: nessun overhead a runtime.

Sistema di tipi strutturale (duck typing): compatibilità basata sulla forma dei dati, non sul nome nominale.

Modalità strict abilitata in tsconfig.json: include noImplicitAny, strictNullChecks e controlli rigorosi.

Tipi primitivi estesi con unknown, never, any, void e tipi unione/intersezione.

unknown è un top type type-safe: richiede narrowing o type assertion prima dell'uso.

never è il bottom type: rappresenta valori che non possono mai verificarsi o computazioni che non terminano.

Type narrowing: raffinamento del tipo tramite typeof, instanceof, in, o custom type guards.

Interfacce estendibili tramite extends e declaration merging; Type alias usati per unioni, tuple e mapped types.

Utility types built-in: Partial, Required, Readonly, Pick, Omit, Record, Extract, Exclude.

Generics permettono di parametrizzare funzioni, classi e tipi mantenendo la correlazione tra tipi di input e output.

Domande tipiche e risposte pronte

Q: Qual è la differenza sostanziale tra type e interface?R: Uso interface per definire la forma di oggetti o classi estendibili tramite ereditarietà o declaration merging. Uso type quando mi servono tipi unione, primitive, tuple o mapped types avanzati.

Q: Perché preferire unknown rispetto ad any?R: any disabilita completamente il type checker annullando la sicurezza. unknown impone di validare o restringere il tipo a runtime prima di accedere a proprietà o metodi.

Q: Come gestisci il type narrowing con tipi discriminati (discriminated unions)?R: Definisco un campo comune costante letterale, tipicamente type o kind, e uso uno switch o if su quel campo per consentire a TypeScript di restringere automaticamente il tipo nel blocco.

Q: Cosa si intende per type erasure?R: Tutti i tipi, interfacce e annotazioni TypeScript vengono rimossi durante la compilazione. Il codice JavaScript risultante a runtime non ha consapevolezza dei tipi TypeScript.

Q: Come rendi esaustivo un controllo di rami (exhaustive check)?R: Assegno il valore residuo a una variabile di tipo never nel blocco default. Se aggiungo un nuovo tipo all'unione senza gestirlo, la compilazione fallisce.

Insidie e errori comuni

Uso improprio delle asserzioni di tipo as Type per silenziare errori del compilatore anziché correggere o verificare il tipo.

Assunzione errata che i tipi TypeScript validino i dati a runtime (es. payload API senza librerie di validazione come Zod).

Dimenticanza di abilitare strictNullChecks, consentendo l'assegnazione silenziosa di null o undefined a qualsiasi tipo.

Confusione tra any (disabilita controlli) e unknown (forza il narrowing esplicito).

Sovrascrittura accidentale causata da declaration merging involontario usando interfacce duplicate con lo stesso nome.

Snippet e comandi

# Inizializzare un progetto TypeScript con tsconfig.json
npx tsc --init

# Compilare il progetto in modalità watch
npx tsc --watch

TypeScript

// Discriminated union e exhaustive checking
type SuccessState = { status: 'success'; data: string[] };
type ErrorState = { status: 'error'; error: Error };
type LoadingState = { status: 'loading' };

type State = SuccessState | ErrorState | LoadingState;

function handleState(state: State): string {
  switch (state.status) {
    case 'success':
      return `Loaded ${state.data.length} items`;
    case 'error':
      return state.error.message;
    case 'loading':
      return 'Loading...';
    default: {
      const _exhaustiveCheck: never = state;
      return _exhaustiveCheck;
    }
  }
}

TypeScript

// Custom Type Guard con predicato di tipo
interface User {
  id: string;
  name: string;
}

function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'name' in value &&
    typeof (value as Record<string, unknown>).id === 'string' &&
    typeof (value as Record<string, unknown>).name === 'string'
  );
}

TypeScript

// Utility types e Generics con vincoli (constraints)
type ApiResponse<T> = {
  data: T;
  timestamp: number;
};

interface Entity {
  id: string;
}

function updateEntity<T extends Entity>(original: T, updates: Partial<Omit<T, 'id'>>): T {
  return { ...original, ...updates };
}

Da ripassare

[x] Regole base di type inference e annotazioni primitive

[x] Differenza pratica tra type e interface

[ ] Mapped types e conditional types (infer, Exclude, Extract)

[ ] Validazione a runtime di contratti API con Zod o TypeBox

[ ] Configurazione ottimale dei flag di strictness in tsconfig.json

Jarvis Interviews
Jarvis