Conditionele types in TypeScript maken het mogelijk om één type te beschrijven via een ander op basis van T extends U ? X : Y. Dergelijke types bieden krachtige mogelijkheden bij het bouwen van complexe type-logica, vooral in bibliotheken en API-declaraties.
Voorbeeld van een basis conditioneel type:
type IsString<T> = T extends string ? 'ja' : 'nee'; type A = IsString<string>; // 'ja' type B = IsString<number>; // 'nee'
Wanneer een conditioneel type wordt toegepast op een union-type, "verdeelt" TypeScript de voorwaarde over elk element van de union individueel. Dit wordt distributief gedrag van conditionele types genoemd.
Voorbeeld:
type Foo<T> = T extends { id: number } ? string : boolean; type Result = Foo<{id: number} | {name: string}>; // string | boolean
Dit is een zeer krachtige functie, maar kan verwarring veroorzaken met de verwachte resultaten, vooral bij het werken met arrays en het mappen van types.
Wikkel het type in een tuple:
type NoDistrib<T> = [T] extends [{id: number}] ? string : boolean; type Result = NoDistrib<{id: number} | {name: string}>; // boolean
Vraag: "Wat gebeurt er als je een conditioneel type met union-types gebruikt zonder deze in tuples te wikkelen? Is het resultaat altijd hetzelfde als bij gewone logica?"
Antwoord: Het resultaat kan onverwacht zijn! Vanwege de distributiviteit wordt de voorwaarde afzonderlijk toegepast op elk lid van de union-types. Om de volledige union-type strikt te vergelijken, moet een wikkeling (tuple) worden gebruikt.
Voorbeeld:
type Test<T> = T extends string ? number : boolean; type A = Test<string | boolean>; // number | boolean, niet alleen boolean
Verhaal
In een bibliotheek voor serialisatie werd een conditioneel type gebruikt om de datastructuur te controleren, maar men vergat de generic parameter in een tuple te wikkelen. Als gevolg hiervan faalden de declaraties op complexe union-types, en de compiler gaf onvoorspelbare types bij het gebruik van de API.
Verhaal
Bij het proberen te implementeren van een type transformatie voor het verwerken van modellenvelden ging er informatie verloren: door de distributiviteit die unions genereert, vergat men een paar logica-takken te behandelen, en uiteindelijk werd de type-definitie te permissief.
Verhaal
Een ontwikkelaar dacht dat T extends SomeType binnen conditionele types zou werken zoals we verwachten voor het gehele object, maar het verstuiving gebeurde — de compiler gaf een inconsistentie aan, en type-chaos en ernstige bugs bij het genereren van autodocumentatie begonnen.