I am trying to implement a function returning different type of value based on its parameter.
I could do it with just using javascript, but I want Typescript to correctly infer return type of this function too. So I used Generic to extract return type, but TS(2322) error is reported for every return statement.
Is there any better way to correctly use TypeScript to type function's return value?
Simplified version of my code below.
type Context = "Acontext" | "Bcontext";
type A = "a";
type B = "b";
type AorB<T extends Context> = T extends "Acontext"
? A
: T extends "Bcontext"
? B
: never;
const testFunction = <T extends Context>(context: T): AorB<T> => {
const aObject: A = "a";
const bObject: B = "b";
switch (context) {
case "Acontext":
return aObject;
case "Bcontext":
return bObject;
default:
throw new Error(`Invalid Context Type : ${context}`);
}
};
Here's My codesandbox link. https://codesandbox.io/s/nostalgic-fast-5ixmsl?file=/src/index.ts:0-477
CodePudding user response:
TypeScript does not support conditional types in a place of return type. IN order to achieve it, you should use function overloading.
type Context = "Acontext" | "Bcontext";
type A = "a";
type B = "b";
type AorB<T extends Context> = T extends "Acontext"
? A
: T extends "Bcontext"
? B
: never;
function testFunction<T extends Context>(context: T): AorB<T>
function testFunction<T extends Context>(context: T) {
const aObject: A = "a";
const bObject: B = "b";
switch (context) {
case "Acontext":
return aObject;
case "Bcontext":
return bObject;
default:
throw new Error(`Invalid Context Type : ${context}`);
}
};
const result1 = testFunction('Acontext') // "a"
const result2 = testFunction('Bcontext') // "b"
However, it is not super safe. This code still will be allowed by TS
switch (context) {
case "Acontext":
return bObject; // wrong
case "Bcontext":
return aObject; // wrong
default:
throw new Error(`Invalid Context Type : ${context}`);
}
CodePudding user response:
Another possibilty is to refactor the code a bit to use a simple mapping of input and output in the form of an object.
type AorB<T extends Context> = {
"Acontext": A
"Bcontext": B
}[T]
const testFunction = <T extends Context>(context: T): AorB<T> => {
const aObject: A = "a";
const bObject: B = "b";
const result = {
"Acontext": aObject,
"Bcontext": bObject
}[context]
if (!result) throw new Error(`Invalid Context Type : ${context}`)
return result
};
Now we have no type errors and also slightly better readability. Adding new branches will also be easier.
There is also the benefit of better type safety since this throws an error now:
const result = {
"Acontext": bObject,
"Bcontext": aObject
}[context]