Change function return type if optional argument is passed in typescript

Viewed 216

I have the following function:

const lists = {mylist: ["alpha", "beta", "gamme"]};

function foo(listName, element) { 
    if (typeof element !== 'undefined') {
        return !!lists[listName].find(x => x === element);
    }

    return [lists[listName], () => refreshLists()];
}

foo('mylist'); // returns [string[], () => any]
foo('mylist', 'alpha') // returns boolean

As you can see the function has 2 different return types depending if argument element is set. How can I represent that properly with typescript, so that the editor chooses correct return type depending on how much arguments were passed?

My current solution looks like this but is not recognized by the editor:

export type FnReturnType<T> = [T] extends [undefined] ? [string[], () => any] : boolean;

function foo<T extends string | undefined>(listName: string, element: T): FnReturnType<T> { 
    if (typeof element !== 'undefined') {
        return !!lists[listName].find(x => x === element);
    }

    return [lists[listName], () => refreshLists()];
}

How can I make a conditional type that return boolean if typeof element !== undefined but otherwise [string[], () => any]?

1 Answers

Use function overloading.

You write two separate overload signatures for a different argument types and return types and then you write function implementation that will return a correct value.

function foo(listName: string): [string[], () => any]; // overload signature #1
function foo(listName: string, element: string): boolean; // overload signature #2
function foo(listName: string, element?: string): boolean | [string[], () => any] { // function implementation
  if (typeof element !== 'undefined') {
      return !!lists[listName].find(x => x === element);
  } 
  
  return [lists[listName], () => refreshLists()];
}

Check playground

Related