Search code examples
typescriptconditional-types

Change function return type if optional argument is passed in typescript


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]?


Solution

  • 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