Il Narrowing dei tipi (Type Narrowing) in TypeScript è un processo in cui il compilatore "capisce" che in una certa sezione di codice una variabile ha un tipo più specifico, a seconda delle condizioni.
Tecniche comuni di narrowing:
typeof:function example(x: number | string) { if (typeof x === 'string') { // x: string qui return x.toUpperCase(); } else { // x: number qui return x.toFixed(2); } }
instanceof (per classi):if (dateObj instanceof Date) { // dateObj: Date }
null e undefined:function print(value?: string) { if (value != null) { // value: string console.log(value.length); } }
type Pet = { kind: 'dog'; woof: () => void } | { kind: 'cat'; meow: () => void }; function sound(pet: Pet) { if (pet.kind === 'dog') { pet.woof(); } else { pet.meow(); } }
TypeScript supporta anche funzioni predicato personalizzate:
function isString(x: unknown): x is string { return typeof x === 'string'; }
Il narrowing rende i controlli dei tipi più sicuri e il codice più affidabile.
È possibile garantire il narrowing del tipo tramite confronti normali (ad esempio,
==/===), e funziona sempre?
Risposta: No. Non in tutti i casi TypeScript comprende il tipo da confronti semplici, specialmente se il confronto è troppo "vago" o avviene tramite variabili/proprietà indirette. Per il narrowing è spesso necessario utilizzare meccaniche esplicite (typeof, instanceof, proprietà discriminatorie e type guard).
Esempio:
function foo(x: number | string | null) { if (x) { // x: string | number, null non è più possibile, ma non ci sarà un narrowing a specifico } }
Storia
In un grande progetto TypeScript, la condizione di tipo user.role == 'admin' non ha ristretto il tipo di dati, e sono state comunque necessarie verifiche per l'esistenza della proprietà. Gli sviluppatori hanno sottovalutato le regole di narrowing, portando a errori "Cannot read property ... of undefined".
Storia
In un'app mobile, una funzione accettava un oggetto o una stringa. Attraverso una chiamata indiretta a una funzione che cambiava tipo, il narrowing non avveniva e su alcuni dispositivi si verificava un crash durante la chiamata a un metodo assente nella stringa. I test sono falliti in casi rari.
Storia
Durante la migrazione di codice da JavaScript a TypeScript, non sono state implementate le proprie funzioni type guard, supponendo che i controlli sulle proprietà avrebbero sempre ristretto il tipo. Di conseguenza, oggetti complessi con campi opzionali si comportarono in modo errato e durante il runtime si verificarono errori di accesso ai dati imprevedibili.