Issue
While reading the section on Conditional Types in the official Typescript Handbook, I came across this behavior in Typscript that I did not understand.
Paraphrasing the example given in the section, here's my example:
type ConditionalType<T> = T extends number ? "SUCCESS" : "FAIL"; // conditional type defined using generics
// source of confusion
type Result1 = (string | number) extends number ? "SUCCESS" : "FAIL"; // Result1 is "FAIL"
type Result2 = ConditionalType<string | number>; // Result2 is "FAIL" | "SUCCESS"
// sanity check
type Result3 = ConditionalType<number>; // Result3 is "SUCCESS"
type Result4 = ConditionalType<boolean>; // Result4 is "FAIL"
Here's a recreation of the above example on the official Typescript playground: [LINK]
I don't understand why Result1 and Result2 are different. According to me, Result2 should also have been "FAIL".
Solution
This is a consequence of a section a bit lower namely Distributive conditional types
Basically if typescript sees a condition over a naked type parameters (such as T) and if T is a union, it will apply the conditional type to each constituent of the union and union the results.
So we have:
type Result2 = ConditionalType<string | number>
≡ ConditionalType<string> | ConditionalType<number>
≡ (string extends number ? "SUCCESS" : "FAIL") | (number extends number ? "SUCCESS" : "FAIL")
≡ "FAIL" | "SUCCESS"
This will not happen for Result1 where the condition is over a type not a type parameter, so inlining a conditional type can produce different results.
We can get the same behavior if we introduce a type parameter in the inlined version:
type Result1 = (string | number) extends infer T ? T extends number ? "SUCCESS" : "FAIL" : never; // Result1 is "FAIL" | "SUCCESS"
In the example above, infer T will introduce a new type parameter that will contain the type on the left side of extends in this case string | number
We can also disable distribution for the conditional type if we wrap the type parameter in a tuple:
type ConditionalType<T> = [T] extends [number] ? "SUCCESS" : "FAIL"; // conditional type defined using generics
type Result2 = ConditionalType<string | number>; // Result2 is "FAIL"
// sanity check
type Result3 = ConditionalType<number>; // Result3 is "SUCCESS"
type Result4 = ConditionalType<boolean>; // Result4 is "FAIL"
Answered By - Titian Cernicova-Dragomir
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.