Home > Back-end >  Return type based on the type of a parameter
Return type based on the type of a parameter

Time:10-23

I have this: (see playground)

const TEST1 = "TEST1";
type TEST1 = "TEST1";

const TEST2 = "TEST2";
type TEST2 = "TEST2";

interface ActionTypeMaps {
    TEST1: undefined;
    TEST2: string;
}

export interface IAction<T extends keyof ActionTypeMaps> {
  type: T;
  payload: ActionTypeMaps[T];
}

export interface IActionWithoutPayload<T extends keyof ActionTypeMaps> {
  type: T;
  payload: undefined;
}

export function action<T extends keyof ActionTypeMaps>(
  type: T
): IActionWithoutPayload<T>;

export function action<T extends keyof ActionTypeMaps>(
  type: T,
  payload: ActionTypeMaps[T]
): IAction<T>;

export function action<T extends keyof ActionTypeMaps>(
  type: T,
  payload?: ActionTypeMaps[T]
): IActionWithoutPayload<T> | IAction<T> {
  if (payload === undefined) {
    return { type, payload: undefined }; 
  }

  return {
    payload,
    type,
  };
}

This almost works you can do action(TEST1) and it's happy and you can do action(TEST1, "test") and then it will complain.

But you can also still do action(TEST2) which it shouldn't like. Here it should require you to pass a string as the second argument.

I need a way to say you can only call action() with one parameter when ActionTypeMaps[T] is undefined (or whatever other "not there" type you want to use).

CodePudding user response:

This is achievable by varying the amount of parameters it takes like this:

export function action<T extends keyof ActionTypeMaps>(
  ...[type, payload]: [ActionTypeMaps[T]] extends [never] ? [type: T] : [type: T, payload: ActionTypeMaps[T]]
): IActionWithoutPayload<T> | IAction<T> {

Just a small change to your map: I'd use never instead of undefined:

interface ActionTypeMaps {
    TEST1: never;
    TEST2: string;
}

Playground

  • Related