Search code examples
typescriptoverloadingdiscriminated-union

Typescript can't correctly infer type of parameter inside function overload


When calling function foo the correct types are enforced but inside the function i would expect that inside the if statement typescript would correctly infer that age is a string when name equals "a". Is there another way to do this?

function foo(name: "a", age: string): void;
function foo(name: "b", age: number): void;
function foo(name: "a" | "b", age: string | number) {
  if (name === "a") {
     const x = age; // Would expect x to be string but is string | number;
  }
}
foo("a", "asd"); // Correct
foo("a", 5); // Error
foo("b", 5); // Correct
foo("b", "asd"); // Error

Solution

  • I run into this problem all the time and it is why I try to avoid function overloads in TypeScript. This isn't really how they work, since there is just one implementation the TS compiler doesn't discriminate the function arguments. I imagine this has to do with backwards-compatibility but it sure is a pain.

    The only workaround I have found is explicitly using discriminated unions in the function signature--but this requires defining the arguments as spread.

    function foo(...[name, age]: [name: "a", age: string] | [name: "b", age: number]) {
      if (name === "a") {
        const x = age;
        //    ^? const x: string
      }
      if (name === "b") {
        const x = age; 
        //    ^? const x: number
      }
    }
    

    TypeScript Playground link

    I don't know of any issue on the TypeScript GitHub that addresses this but there may be one.