TypeScript: Cannot overload interface method with different return types

Bug Report

Defining an overloaded method in an interface raises an error during implementation if overloads have different return types.

πŸ”Ž Search Terms

overload, method, interface, factory function

πŸ•— Version & Regression Information

Doesn’t work in v3, v4 or v5.

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

interface Shape {
  area(x: number): number;
  area(x: number, y: number): string;
}

πŸ™ Actual behavior

class ShapeImpl implements Shape {
  // ❌
  area(_x: number, _y?: number): number | string {
    return 0;
  }
}

function createShape(): Shape {
  return {
    // ❌
    area(_x: number, _y?: number): number | string {
      return 0;
    },
  };
}

πŸ™‚ Expected behavior

class ShapeImpl implements Shape {
    // βœ…
  area(_x: number, _y?: number): number | string {
    return 0;
  }
}

function createShape(): Shape {
  return {
    // βœ…
    area(_x: number, _y?: number): number | string {
      return 0;
    },
  };
}

More details

If the return type does not change, overloads work as expected.

βœ…

interface Shape {
  area(x: number): number;
  area(x: number, y: number): number; // πŸ‘ˆ
}

class ShapeImpl implements Shape {
  area(_x: number, _y?: number): number {
    return 0;
  }
}

function createShape(): Shape {
  return {
    area(_x: number, _y?: number): number {
      return 0;
    },
  };
}

Also works if the overloads are defined in the class or on root level functions

βœ…

class Shape {
  area(x: number): number;
  area(x: number, y: number): string;
  area(_x: number, _y?: number): number | string {
    return 0;
  }
}

function area(x: number): number;
function area(x: number, y: number): string;
function area(_x: number, _y?: number): number | string {
  return 0;
}

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 14
  • Comments: 17 (8 by maintainers)

Most upvoted comments

Ok, but, whats the difference here?

// ❌ doesn't work
interface Shape {
  area(x: number): number;
  area(x: number, y: number): string;
}
class ShapeImpl implements Shape {
  area(_x: number, _y?: number): number | string {
    return 0;
  }
}
const num: number = (new ShapeImpl()).area(3);
const str: string = (new ShapeImpl()).area(3, 3);

^ playground link

// βœ… works
class ShapeImpl {
  area(x: number): number;
  area(x: number, y: number): string;
  area(_x: number, _y?: number): number | string {
    return 0;
  }
}
const num: number = (new ShapeImpl()).area(3);
const str: string = (new ShapeImpl()).area(3, 3);

^ playground link

Essentially #47669 (focuses on arrow functions but it’s the same issue: we currently have no way to say β€œcheck this the same loose way that overload implementation are checked”)

implements clauses don’t change the shape of your class; this fact is intentional. IOW, for these two snippets

class ShapeImpl implements Shape {
  area(_x: number, _y?: number): number | string {
    return 0;
  }
}

and

class ShapeImpl {
  area(_x: number, _y?: number): number | string {
    return 0;
  }
}

ShapeImpl.area has the same type in both.