ProgrammazioneFrontend Developer

Come funziona il meccanismo di tipizzazione degli union types in TypeScript? A cosa serve, come funziona il narrowing del tipo e quali sono le peculiarità dell'uso degli union types?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Storia della questione:

Gli union types sono stati introdotti in TypeScript per descrivere variabili e parametri che possono assumere valori di diversi tipi. Questa possibilità ha notevolmente ampliato la flessibilità della tipizzazione rispetto ai linguaggi classici.

Problema:

In JavaScript, le funzioni e le variabili sono spesso multi-formato (ad esempio, accettano numeri o stringhe), il che complica la tipizzazione sicura. Senza gli union types, era necessario utilizzare any o duplicare il codice. Questo aumentava il numero di errori in produzione e complicava il lavoro in grandi team.

Soluzione:

Gli union types permettono di dichiarare una variabile che può essere di uno dei diversi tipi, garantendo operazioni corrette dopo un controllo. È inoltre supportato il narrowing del tipo, che aiuta il compilatore a "capire" con cosa ha a che fare.

Esempio di codice:

function formatId(id: number | string): string { if (typeof id === 'string') { return id.toUpperCase(); } return id.toString(); }

Caratteristiche chiave:

  • Permettono di specificare esplicitamente i tipi possibili delle variabili e dei parametri.
  • Funzionano insieme al meccanismo di narrowing del tipo tramite controlli (ad esempio, typeof o in).
  • Complicano la gestione degli oggetti, dove una stessa chiave può avere tipi diversi — è necessaria una gestione attenta della logica di accesso.

Domande trabocchetto.

È possibile scrivere un'unione di oggetti con proprietà diverse e accedere a qualsiasi proprietà senza controllare?

No. Gli union types permettono di accedere solo a quelle proprietà e metodi che sono presenti in tutti i tipi. Per accedere a proprietà specifiche è necessario il narrowing del tipo.

Esempio di codice:

type Fish = { swim: () => void; }; type Bird = { fly: () => void; }; function move(animal: Fish | Bird) { // animal.swim(); // Errore senza narrowing if ('swim' in animal) { animal.swim(); // OK } }

Perché non tutti i metodi sono disponibili per l'union type string | number?

TypeScript consente negli union solo ciò che è presente in tutti i tipi inclusi. Per metodi specifici è necessario prima controllare il tipo reale.

Cosa succede se non si verifica il tipo in un'unione, ma si prova a chiamare un metodo specifico?

In tal caso si verificherà un errore di compilazione, poiché non è garantita la presenza del metodo. Funziona solo dopo il controllo di un tipo specifico.

Errori di tipo e anti-pattern

  • Saltare il controllo del tipo prima dell'utilizzo (può causare un errore di accesso).
  • Descrivere union types troppo ampi (si perde la sicurezza tipologica).
  • Un errato narrowing porta a errori invisibili e not exhaustive checks.

Esempio dalla vita reale

Caso negativo

È stato dato un tipo string | number a una variabile e senza controllo è stato fatto toUpperCase(). Di conseguenza, l'applicazione si interrompe con dati numerici.

Vantaggi:

  • Codice scritto rapidamente.

Svantaggi:

  • Errori in runtime.
  • Perdita di fiducia nella tipizzazione statica di TypeScript.

Caso positivo

Controlliamo il tipo prima di lavorare con il metodo:

if (typeof value === 'string') { return value.toUpperCase(); } else { return value.toString(); }

Vantaggi:

  • Completa sicurezza nella fase di compilazione.
  • Maggiore manutenibilità del codice.

Svantaggi:

  • Necessità di scrivere controlli aggiuntivi.