I tipi condizionali (conditional types) in TypeScript consentono di descrivere un tipo attraverso un altro secondo il principio T extends U ? X : Y. Questi tipi offrono potenti possibilità nella costruzione di logiche di tipo complesse, specialmente nelle librerie e nelle dichiarazioni API.
Esempio di tipo condizionale di base:
type IsString<T> = T extends string ? 'yes' : 'no'; type A = IsString<string>; // 'yes' type B = IsString<number>; // 'no'
Se un tipo condizionale viene applicato a un tipo union, TypeScript "distribuisce" la condizione su ciascun elemento dell'unione separatamente. Questo è chiamato comportamento distribuitivo dei tipi condizionali.
Esempio:
type Foo<T> = T extends { id: number } ? string : boolean; type Result = Foo<{id: number} | {name: string}>; // string | boolean
Questa è una caratteristica molto potente, ma può causare confusione con il risultato atteso, specialmente quando si lavora con array e mappatura dei tipi.
Avvolgiamo il tipo in una tupla:
type NoDistrib<T> = [T] extends [{id: number}] ? string : boolean; type Result = NoDistrib<{id: number} | {name: string}>; // boolean
Domanda: "Cosa succede se si usa un tipo condizionale con tipi union senza avvolgerli in tuple? Il risultato è sempre lo stesso che nella logica normale?"
Risposta: Il risultato può essere inaspettato! A causa della distribuzione, la condizione viene applicata a ciascun membro dei tipi union separatamente. Per confrontare rigorosamente l'intero tipo union, è necessario utilizzare un involucro (tupla).
Esempio:
type Test<T> = T extends string ? number : boolean; type A = Test<string | boolean>; // number | boolean, non solo boolean
Storia
Nella libreria per la serializzazione è stato utilizzato un tipo condizionale per controllare la struttura dei dati, ma ci si è dimenticati di avvolgere il parametro generico in una tupla. Di conseguenza, sulle dichiarazioni di tipi union complessi si sono rotte, e il compilatore ha restituito tipi imprevedibili nell'uso dell'API.
Storia
Nel tentativo di implementare una trasformazione di tipo per la gestione dei campi dei modelli, si è verificata la perdita di parte delle informazioni: a causa del fatto che la distribuzione genera unioni, alcune ramificazioni della logica sono state dimenticate, risultando in una tipizzazione troppo permissiva.
Storia
Un sviluppatore pensava che T extends SomeType all'interno dei tipi condizionali si comportasse come ci si aspetta per l'intero oggetto, ma si è verificata una "disperazione" — il compilatore ha segnalato incoerenze, e sono iniziati il tipo caos e gravi bug nella generazione della documentazione automatica.