Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple function with conditional type

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.

like image 780
John Avatar asked Sep 03 '18 07:09

John


People also ask

What is conditional sentence type 1 example?

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.

What is a Type 3 conditional?

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.

How do you use conditional types in JavaScript?

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.

Why do we use conditional types in Python?

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.


2 Answers

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
like image 37
artem Avatar answered Oct 10 '22 03:10

artem


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)
}
like image 57
Titian Cernicova-Dragomir Avatar answered Oct 10 '22 03:10

Titian Cernicova-Dragomir