Home > Back-end >  How to Correctly Type Return type of function by its parameter
How to Correctly Type Return type of function by its parameter

Time:08-02

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}`);
  }
};

This is the error message. enter image description here

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"

Playground

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]

Playground

  • Related