Skip to content

Method overload doesn't resolve correctly resolve when using union type #40181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mhofman opened this issue Aug 21, 2020 · 2 comments
Closed
Labels
Duplicate An existing issue was already created

Comments

@mhofman
Copy link

mhofman commented Aug 21, 2020

TypeScript Version: 4.0.2

Search Terms: overload union type callback

Expected behavior: No Type error

Actual behavior: Argument of type '"change"' is not assignable to parameter of type '"focus"'

Related Issues: Possibly #39692

Code

interface ChangeEvent {
  type: 'foo' | 'bar';
  empty: boolean;
}

interface FooChangeEvent extends ChangeEvent {
  type: 'foo';
  fooInfo: string;
}

interface BarChangeEvent extends ChangeEvent {
  type: 'bar';
  barInfo: number;
}

interface Base {
  clear(): Base;
}

interface Foo extends Base {
  on(eventType: 'focus', handler: () => void): Foo;
  on(eventType: 'change', handler: (event: FooChangeEvent) => void): Foo;
}

interface Bar extends Base {
  on(eventType: 'focus', handler: () => void): Bar;
  on(eventType: 'change', handler: (event: BarChangeEvent) => void): Bar;
}

let foo: Foo = ({} as Foo);
foo.on('focus', () => {});
foo.on('change', () => {});

let bar: Bar = ({} as Bar);
bar.on('focus', () => {});
bar.on('change', () => {});

function handleEvents(obj: Foo | Bar) {
  obj.on('focus', () => {});
  obj.on('change', () => {});
}
Output
"use strict";
let foo = {};
foo.on('focus', () => { });
foo.on('change', () => { });
let bar = {};
bar.on('focus', () => { });
bar.on('change', () => { });
function handleEvents(obj) {
    obj.on('focus', () => { });
    obj.on('change', () => { });
}
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": 2,
    "target": "ES2017",
    "jsx": "React",
    "module": "ESNext"
  }
}

Playground Link: Provided

Details

Removing the overload (commenting out the focus eventType in the example), work fine, so TS is able to resolve the union type for the handler.

Adding an event argument as a union FooChangeEvent | BarChangeEvent to the change's handler doesn't help the compiler resolve the overload.

Defining a base signature for the overloaded method only helps if using Base and not a union of subtypes, which isn't acceptable if one wants to only accept a subset of subtypes.

@RyanCavanaugh
Copy link
Member

Duplicate #9239, basically. Call signatures from different declarations in union types can only be merged when they have sufficiently similar parameter types, and the differing argument types to change means that overload can't be merged. This process attempts to yield the best set of possible output overloads, so in this case change gets dropped.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Aug 21, 2020
@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants