TypeScript utilizza tipizzazione strutturale (structural typing), o "tipizzazione per tipo anatra". Per la compatibilità dei tipi è importante la struttura (firma), non il nome o l'origine del tipo.
Esempio:
interface Point2D { x: number; y: number; } interface Coord2D { x: number; y: number; } // Questi tipi sono intercambiabili: Point2D e Coord2D, perché la struttura è la stessa. const foo: Point2D = { x: 1, y: 2 }; const bar: Coord2D = foo; // OK!
Tipizzazione nominale (nominal typing): per la compatibilità dei tipi è importante il "nome" o "fabbrica", la struttura non è rilevante.
In TypeScript la tipizzazione nominale non è supportata nativamente, ma può essere emulata utilizzando i branded types:
type USD = number & { readonly __brand: unique symbol } type EUR = number & { readonly __brand: unique symbol } let priceUSD: USD; let priceEUR: EUR; // priceUSD = priceEUR; // Errore! Brand diversi.
Perché applicarlo? Ad esempio, per distinguere tipi strutturalmente uguali ma semanticamente diversi — valute, userID/tokenID, grandezze fisiche, ecc.
Domanda: Perché il seguente codice si compila senza errori, anche se Address e UserId sono logica tipi diversi?
interface Address { value: string; } interface UserId { value: string; } let id: UserId = { value: "test" }; let addr: Address = id; // OK
Risposta: Perché in TypeScript conta la struttura, non il nome del tipo. Entrambi i tipi sono semplicemente "oggetti con value: string".
Storia
Progetto: Sistema finanziario con calcoli in USD/EUR. Le somme venivano trasmesse tramite number. Una volta sono state scambiate le valute durante l'addizione — a causa della tipizzazione strutturale TypeScript non lo ha rilevato. In seguito sono stati introdotti i branded types per escludere tali errori in fase di compilazione.
Storia
Progetto: Durante lo sviluppo di un'API REST, sono stati utilizzati oggetti per gli ID di entità diverse (userId, groupId), entrambi con campo value: string. Per errore userId è stato utilizzato al posto di groupId, e solo la logica di business sul server ha rilevato l'errore.
Storia
Progetto: Nella libreria di parser per DSL sono state utilizzate strutture identiche (type value = { kind: 'num'|'str', value: number|string }). Strutture simili si sono mescolate tra diverse parti del codice, causando errori logici. Sono stati aggiunti campi brand artificiali per separarle.