Return variable types based on string literal type argument

Can I have the return type of a variable based on the value of a string literal type argument in TypeScript 1.8 or 2.0?

type Fruit = "apple" | "orange" function doSomething(foo : Fruit) : string | string[] { if (foo == "apple") return "hello"; else return ["hello","world"]; } var test : string[] = doSomething("orange"); 

Error: TS2322: Enter the string | string [] 'is not assigned to the type' String [] '.

+13
typescript
source share
3 answers

Yes, you can. You just need to check the test variable with instanceof . Typescript then restricts the type.

 type Fruit = "apple" | "orange" function doSomething(foo: Fruit): string | string[] { if (foo == "apple") return "hello"; else return ["hello","world"] } // here the type is still inferred as: string | string[] var test = doSomething("orange"); if (test instanceof String) { // TypeScript knows test is type: string doSomethingWithString(test); } else { // TypeScript knows test is type: string[] doSomethingWithStringArray(test); } function doSomethingWithString(input: string) {} function doSomethingWithStringArray(input: string[]) {} 

UPDATE

Instead, you can simply use this method.

 function doSomething<T>(foo: Fruit): T { if (foo == "apple") return "hello"; else return ["hello","world"] } var test1 = doSomething<string>("apple"); var test2 = doSomething<string[]>("orange"); 

Or another option is to invert the stream like this:

 type Fruit = "apple" | "orange" function doSomething(foo: Fruit): void { if (foo == "apple") doSomthingWithString("hello"); else doSomethingWithStringArray(["hello","world"]); } function doSomethingWithString(input: string) {} function doSomethingWithStringArray(input: string[]) {} 

UPDATE

In fact, I think John White is a much better answer.

+1
source share

Yes, you can use overload signatures to achieve exactly what you want:

 type Fruit = "apple" | "orange" function doSomething(foo: "apple"): string; function doSomething(foo: "orange"): string[]; function doSomething(foo: Fruit): string | string[] { if (foo == "apple") return "hello"; else return ["hello", "world"]; } let test1: string[] = doSomething("orange"); let test2: string = doSomething("apple"); 

Live demo on TypeScript Playground

Attempting to assign doSomething("apple") test1 will result in a compile-time type error:

 let test1: string[] = doSomething("apple"); // ^^^^^ // type string is not assignable to type string[] 

It is important to note that the definition of the overload signature used must always be done manually when implementing the function, and the implementation of the function must support all overload signatures .

TypeScript does not have separate implementations for each overload, as, for example, in C #. Therefore, I find it appropriate to strengthen TypeScript type checks at runtime, for example:

 switch (foo) { case "apple": return "hello"; case "orange": return ["hello", "world"]; default: throw new TypeError("Invalid string value."); } 
+27
source share

I have a better approach. use a generic type, which is then used as the type of the argument (so you won’t need to pass the generic type manually, and typing will output it automatically). Then you can use this type and choose the correct return type.

 type Fruit = 'apple' | 'orange'; function doSomething<P extends Fruit>(foo: P): ({ apple: string; orange: string[] })[P] { if (foo === 'apple') return 'hello'; return ['hello','world]; } const x: string = doSomething('apple'); const y: string[] = doSomething('orange'); 

Thus, you can change the return type of your function based on the argument passed automatically.

+1
source share

All Articles