Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript return type depending on parameter

I am trying to write a function which takes a parameter of type boolean and returns one of two types, depending on the value of the input. I have found two approaches:

function dependsOnParameter<B extends boolean>(x: B): B extends true ? number : string {     if (x) {         return 3;     } else {         return "string";     } } 

Here, TypeScript says that Type '3'/'"string"' is not assignable to type 'B extends true ? number : string'.

My other approach looks like this:

function dependsOnParameter(x: true): number; function dependsOnParameter(x: false): string; function dependsOnParameter(x: boolean): number | string {     if (x) {         return 3;     } else {         return "string";     } } 

This compiles; however, if I try to use my function:

function calling(x: boolean) {     dependsOnParameter(x); } 

I get Argument of type 'boolean' is not assignable to parameter of type 'false'.

Is there any way to achieve what I want without using any?

like image 496
Lukor Avatar asked Oct 15 '18 13:10

Lukor


People also ask

How do I specify a return type of function in TypeScript?

To define the return type for the function, we have to use the ':' symbol just after the parameter of the function and before the body of the function in TypeScript. The function body's return value should match with the function return type; otherwise, we will have a compile-time error in our code.

Can you pass a type as a parameter TypeScript?

To type a function as a parameter, type the function's parameter list and its return value, e.g. doMath: (a: number, b: number) => number . If the function's definition becomes too busy, extract the function type into a type alias.

What does () => void mean TypeScript?

According to the TypeScript docs: void represents the return value of functions which don't return a value. Whenever you see a function returning void , you are explicitly told there is no return value. All functions with no return value have an inferred return type of void .

What is call signature in TypeScript?

In TypeScript we can express its type as: ( a : number , b : number ) => number. This is TypeScript's syntax for a function's type, or call signature (also called a type signature). You'll notice it looks remarkably similar to an arrow function—this is intentional!


2 Answers

Both approaches are valid. If your function uses conditional types in the return it will need to use type assertions, as typescript will not try to reason about the conditional type since it contains a free type parameter:

function dependsOnParameter<B extends boolean>(x: B): B extends true ? number : string {     if (x) {         return 3 as any;     } else {         return "string"as any;     } } 

This approach uses any which you want to avoid.

The second approach we can get to work without resorting to type assertions by just duplicating the last signature:

function dependsOnParameter(x: true): number; function dependsOnParameter(x: false): string; function dependsOnParameter(x: boolean): number | string function dependsOnParameter(x: boolean): number | string {     if (x) {         return 3;     } else {         return "string";     } }  function calling(x: boolean) {     dependsOnParameter(x); // returns number| string     dependsOnParameter(true); // returns number     dependsOnParameter(false); // returns string } 

The last signature is the implementation signature and is not publicly accessible. You can make it accessible by duplicating it. The compiler is not smart enough to combine the two overloads with true/false and decide the return type is string|number

Edit

We can also combine the two approaches for fewer signatures:

function dependsOnParameter<B extends boolean>(x: B): B extends true ? number : string  function dependsOnParameter(x: boolean): number | string{     if (x) {         return 3;     } else {         return "string";     } } 
like image 121
Titian Cernicova-Dragomir Avatar answered Oct 04 '22 21:10

Titian Cernicova-Dragomir


This is the right way:

function dependsOnParameter<B extends boolean>(x: B): B extends true ? number : string {     return (x === true ? 3 : "string") as B extends true ? number : string; } 

Here, the condition itself (B extends true ? number : string) is considered as a type. This type is called a Conditional Type.

like image 20
axell-brendow Avatar answered Oct 04 '22 21:10

axell-brendow