Skip to content
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

Extending multiple interfaces where the interfaces expand on deeper inherited interfaces #34939

Closed
remag9330 opened this issue Nov 6, 2019 · 2 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@remag9330
Copy link

TypeScript Version:
3.6.3-Nightly (this was working in 3.5.2 and prior)

Search Terms:
extends multiple compatible interfaces not identical

Code

interface Events<T> {
    emit<K extends keyof T>(name: K, obj: T[K]): void;
    on<K extends keyof T>(name: K, func: (obj: T[K]) => void): void;
}


interface WidgetEvents {
    onclick: MouseEvent;
}

interface Widget extends Events<WidgetEvents> {
    // ...
}


interface TextboxEvents extends WidgetEvents {
    textchanged: KeyboardEvent;
}

interface Textbox extends Events<TextboxEvents>, Widget {
    // ...
}


declare const w: Widget;
declare const t: Textbox;

w.on("onclick", e => { })
t.on("onclick", e => { })
t.on("textchanged", e => { });

Expected behavior:
Works, no errors

Actual behavior:
Error on the interface Textbox declaration

Interface 'Textbox' cannot simultaneously extend types 'Events<TextboxEvents>' and 'Widget'.
  Named property 'emit' of types 'Events<TextboxEvents>' and 'Widget' are not identical.

and similar for on.

Playground Link:
https://www.typescriptlang.org/play/?ts=3.8.0-dev.20191105&ssl=1&ssc=1&pln=31&pc=1#code/JYOwLgpgTgZghgYwgAgKIDcLgM4B4AqAfMgN4CwAUMtchALbBi4DStAHpCACbbIDWEAJ4B7GMiIAKEHDoQAXMmYAaZMIBGAKwX4A2swC6ASgXphwLgG5KNVSBbtOPfkNHjCUmfMUqYAVxAIChLqWuJ6RsgAvMSm5sbIsZaUAL6UaRSgkLCIKADq5gDmEGAYWGC85FQ0wgEANsAIfAoAssK+2BCl4FYUqRSUmdDwSMj5XEVgDlhOXeW4YxOz2MSVNgD0a8gAdDsp6QPgQzniEBxqwmxLU9y8C8VXqzSQHAgAFnAgRVwKzELncFAuLMen0Dllhih8KcwOc2NcZpgcARobCloQVHdJo9qBttrtevsKFwIAhagCUAgathJgB3BSYnrE0nk5CUkDU5BgbQoi49Sg0rY1CQAIhqpIafGFKhQ0VIyGShkoYEFIBFYvqjSltCiK3liooyqFwueYDeHy+WpluoVFiAA

Related Issues: #8606, #16936

Came across this while upgrading our project from 3.5.2 to 3.7.2, we rely pretty heavily in our typings for a JS library on the above pattern. I looked through the list of breaking changes for TS 3.6
but didn't see anything related to the issue. I note that it is quite similar to #16936 but opted to open a new issue since this was accepted by the compiler in prior versions where that ticket was never considered fixed.

Also, sorry about the title, hopefully the code makes it more obvious what I'm talking about!

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Nov 12, 2019
@RyanCavanaugh
Copy link
Member

This is the intended behavior; prior versions of TS had a bug where certain conflicts were not correctly detected. The types of these properties don't agree and you need to resolve the conflict.

Example:
Setup

interface Events<T> {
    emit<K extends keyof T>(name: K, obj: T[K]): void;
}

interface WidgetEvents {
    onclick: MouseEvent;
}

interface Widget extends Events<WidgetEvents> { }

interface TextboxEvents extends WidgetEvents {
    textchanged: KeyboardEvent;
}

interface Textbox extends Events<TextboxEvents>, Widget { }

The problem:

declare const w: Widget;
// OK
w.emit("onclick", null as any);
// Error
w.emit("textchanged", null as any)

declare const et: Events<TextboxEvents>;
// OK
et.emit("onclick", null as any);
// OK
et.emit("textchanged", null as any);

declare const t: Textbox;
// OK
t.emit("onclick", null as any);
// Is this supposed to be an error or not?
// The two base interfaces disagree
t.emit("textchanged", null as any)

@remag9330
Copy link
Author

Ahh, okay. So we were relying on a bug in older TS versions - that makes sense. We will deal with this internally then. Thanks for your time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

2 participants