Typescript-Literalverbände
Ich habe zwei literale Typen und ich möchte B aus der Vereinigung A ausschließen.
type A = 'a' | 'b'
type B = 'a'
Wenn ich es so mache:
type MyExclude<A2, B2> = A2 extends B2 ? never : A2
type C2 = MyExclude<A, B>
// C2 = 'b'
Es funktioniert korrekt, d.h. ich bekomme 'b'.
Aber wenn ich es direkt so mache:
type C = A extends B ? never : A
// C = 'a' | 'b'
Es tut es nicht. Warum sollte es eine Rolle spielen, ob ich es über einen intermediären generischen Typ oder direkt mache?
Danke.
Antwort #1
Wenn man es als generisch macht, wendet Typescript die Prüfung distributiv auf jedes Mitglied der Vereinigung an. Also wird type C2=MyExclude<A, B> nicht als 'a | b' extends 'a' ? never : 'a | 'b' ausgewertet, sondern eher als ('a' extends 'a' ? never : 'a') | ('b' extends 'a' ? never : 'b'). 'a' erweitert B, daher ergibt das never. 'b' erweitert B nicht, daher ergibt das 'b'. Und das Endergebnis ist never | 'b', was einfach 'b' ist.
Der distributive Teil tritt nicht ein, wenn es kein generischer Typ ist. type C=A extends B ? never : A wird nur einmal ausgewertet, als 'a | b' extends 'a' ? never : 'a | b'. 'a' | 'b' erweitert 'a' nicht, daher ist das Ergebnis der gesamte Typ, 'a' | 'b'
Verteilte bedingte Typen ansehen
Verteilende bedingte Typen
Bedingte Typen, bei denen der überprüfte Typ ein nackter Typparameter ist, werden distributive bedingte Typen genannt. Distributive bedingte Typen werden während der Instanziierung automatisch über Union-Typen verteilt. Zum Beispiel wird eine Instanziierung von
T extends U ? X : Ymit dem TypparameterA | B | CfürTals(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)aufgelöst.