The following function was largely lifted from the typescript handbook section on using conditional types, yet it doesn't work:
function test<T extends boolean>(a: T): T extends true ? string : number {
return a ? '1' : 1
}
Typescript is reporting that:
Type '1 | "1"' is not assignable to type 'T extends true ? string : number'.
Type '1' is not assignable to type 'T extends true ? string : number'.
I imagine I'm missing something obvious. How can I construct this function so that typescript correctly infers the type based on the function's argument?
I realize that this specific problem could be solved using function signature overloading, but I'd like to learn more about conditional types.
Conditional Sentence Type 1. → It is possible and also very likely that the condition will be fulfilled. Form: if + Simple Present, will-Future. Example: If I find her address, I’ll send her an invitation.
The type 3 conditional refers to an impossible condition in the past and its probable result in the past. These sentences are truly hypothetical and unreal, because it is now too late for the condition or its result to exist.
The conditional type syntax uses the ternary operator that we are familiar with from JavaScript code. The extends keyword is used to define the condition. If T2 is within T1 then type A is used; otherwise, type B is used. Conditional types aren't very useful on their own but are extremely useful in generic types.
We just found ourselves using conditional types to apply constraints and then extract out types. This ends up being such a common operation that conditional types make it easier. Conditional types provide us with a way to infer from types we compare against in the true branch using the infer keyword.
TypeScript does correctly infer return type. What it does not do is that it does not check that runtime logic follows the condition specified in conditional type, and in your case it causes compile-time error. You can circumvent the error by using indexed type access to get the type you want based on condition.
It will have different behavior compared to the test
that's declared in your question, namely it will infer union type in case when type is not known at compile time. The implementation will still not be checked for conformance with conditional type logic, but there is no error, and no type assertions necessary:
interface Selector {
t: string;
f: number;
}
function test<T extends boolean>(a: T): Selector[T extends true ? 't' : 'f'] {
// NOTE: not checked that is returns correct type actually
return a ? '1' : 1
}
const t1 = test(true); // string
const t2 = test(false); // number
declare var b: boolean;
const t3 = test(b); // string | number, which may or may not be what you want
The short a answer is you can't. No value will be assignable to an unresolved conditional type (a conditional type that still depends on a free generic type variable). The only thing you can do is use a type assertion.
function test<T extends boolean>(a: T): T extends true ? string : number {
return (a ? '1' : 1) as any
}
Conditional types are useful to express relations between parameters but they don't help when it comes to implementing the function. Another approach would be to use a more permissive implementation signature.
function test<T extends boolean>(a: T): T extends true ? string : number
function test(a: boolean): number | string {
return (a ? '1' : 1)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With