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

Suggestion for Strict interfaces #6184

Closed
blackshadev opened this issue Dec 21, 2015 · 5 comments
Closed

Suggestion for Strict interfaces #6184

blackshadev opened this issue Dec 21, 2015 · 5 comments
Labels
By Design Deprecated - use "Working as Intended" or "Design Limitation" instead

Comments

@blackshadev
Copy link

I encountered a behavior of interface implementations which at first I taught was strange. Consider the following interfaces

interface IEventArgs {}
interface IDispatchable {
    dispatch(type: string, args: IEventArgs): void;
}

Upon implementing this, I expected that typescript would force me to use a matching signature, but it accepts every signature which vaguely matches the implementation.

For example this will not raise an error

// expected
class A implements IDispatchable {
    dispatch(type: string, args: IEventArgs): void {}
}

// according to typescript this is valid as well
class B implements IDispatchable {
    dispatch(): number { return 1; }
}

If I think about it in terms of JavaScript it makes sense that it would accept B. Even though someone would give me N arguments, the function can just ignore them. Moreover even though a function returns a number we can still ignore them.

But from a OO design perspective this is strange (at least to me). The signature of B's method dispatch does not even come close to the expected signature.

Suggestion

Now this is a long shot, but maybe more people support this idea. What if we can mark a interface (or the interface method) as strict. Which would mean that it would only accept implementations with the exact same function signature.

For example

interface IEventArgs {}
strict interface IDispatchable {
    dispatch(type: string, args: IEventArgs): void;
}

// good
class A implements IDispatchable {
    dispatch(type: string, args: IEventArgs): void {}
}

// error
class B implements IDispatchable {
    dispatch(): number { return 1; }
}

Another less elegant solution would be a config flag.

I actually don't have an idea if this is within the operation scope of typescript, but sharing ideas is never a bad thing to do. And maybe the makes of typescript have considered this already and made a specific design decisions, in that case I am curious why.

@RyanCavanaugh
Copy link
Member

The signature of B's method dispatch does not even come close to the expected signature.

There's actually not that much variance allowed. You couldn't have a base dispatch returning string overridden by one returning number, nor dispatch(x: string) overridden by dispatch(x: boolean). In this case B has taken advantage of both aspects of variance.

But there's nothing wrong with B's implementation of dispatch that isn't a plausible design choice by the class author. B is a true substitute for IDispatchable.

Moreover, it's not clear why the definer of IDispatchable gets to choose whether or not its implementors have exact signature matches. Why does it care? Why does anyone care? What bugs are prevented here?

@RyanCavanaugh RyanCavanaugh added the By Design Deprecated - use "Working as Intended" or "Design Limitation" instead label Dec 21, 2015
@blackshadev
Copy link
Author

If it is by design, than that is end of discussion. I just was curious if ther was a reason for it being as it is, which you gave me. I found it strange at first because this isn't allowed in languages like C# and java, where the call signature must match exactly. But as I said, reflecting on how javascript treats extra arguments and return values, it makes sense.

@RyanCavanaugh
Copy link
Member

As an aside, I realized my rhetorical questions at the end there might not have sounded as rhetorical as they were, so apologies for that if it sounded pushy. Thanks!

@blackshadev
Copy link
Author

No probs, I wanted to answer very strongly on them, but after rereading them I realist the actual questions and tone you tried to set. ps. that wikipedia article is a good read.
Thanks again!

@adamcarr
Copy link

adamcarr commented May 3, 2017

I was shocked when a co-worker informed me of this today. I get that B is a valid substitue for IDispatchable; however, if I am using interfaces as contracts within my codebase, if the contract changes, I would like to be "aware" of it as my implementation may not be robust enough to handle the change. It would be nice if there was a compiler flag (or a interface definition keyword like "strict") available to enforce strict adherence to function signatures in interfaces.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
By Design Deprecated - use "Working as Intended" or "Design Limitation" instead
Projects
None yet
Development

No branches or pull requests

3 participants