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

Interface implementation may have more-specific argument types than interface #46999

Closed
twavv opened this issue Dec 3, 2021 · 4 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@twavv
Copy link

twavv commented Dec 3, 2021

Bug Report

🔎 Search Terms

interface, variance (covariance, contravariance), interface methods, implementation, soundness, unsoundness, narrowing, interface assignability

🕗 Version & Regression Information

Seems to effect every version of TypeScript I tried on the Playground (3.X and 4.X).

⏯ Playground Link

https://www.typescriptlang.org/play?ts=4.5.2#code/PTAEFkE9QYwewCYFNQHM5IM6gBZIE5IBQRAlgHYAuBAZgIYwoCqmBoA3kaN6OXQLZIAXKEyV8FVAG4iAXxIVq+eo1ABxQkiUcuaTZQAUmOgBsArpTqVScciLETyqADSgzrfAH4RLNgB9eM34AIwIASntxSRl5InhyMVAaOHx+Uw0kLQIRDKz8UABeHW5UfSNXd2zQX3ww4p4QHiamgD02lt1uRoBlHDgzEwReOEpQUNBTEzgAdyQEEQADGtAA8iDQ-ICzcmQaCjmF0AMafDh+dX1wztBGzD6BofIRsZQ6TExSVD5gkxRKOFASw8hzwhAAdNdCJQzPhyICACTsTCyUD8aDIOiERKIyr4MF8QSyBYyJqNZrkinNdrUtrXRoAdVIJhMoEoOFO0wmoHw22sglABFO+VCMDolTcHlRdGgDGhk2g422u32CDpYBSXLWIQIEO4smcchkcVsmDgvzBU1QxxSaRMuSUYNKmUMACI8My4C7XOxeAJhKAXQAVfB0ABupEwLtAsjCYRk8VN5st1tS6UueKdWgMbqQHpdsZkQA

💻 Code

// My code goes here

interface User {
    name: string;
}

interface Greeter {
  greet(salutation: string, user?: User | number): string;
}

const formalGreeter: Greeter = {
  greet(s, user: User) {
    //           ^^^^
    // Should not be allowed: `User | number | undefined` (from Greeter)
    // should not be assignable to `User` here.
    return `${s} my dearest ${user.name}`;
    //                        ^^^^^^^^^
    // Will throw a runtime error because user may actually be undefined
    // or a number.
  },
};

console.log(formalGreeter.greet("hello", { name: "Travis" }));
console.log(formalGreeter.greet("hello"));

🙁 Actual behavior

Should not typecheck (see comments above). The implementation given should not be assignable to Greeter.

🙂 Expected behavior

Typechecks successfully.

@RyanCavanaugh
Copy link
Member

https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-function-parameters-bivariant

@twavv
Copy link
Author

twavv commented Dec 3, 2021

Thanks! I suspected it was probably something like that, just didn't quite know how to search for it. Hopefully this will help other people find the issue.

At the very least I wish this behavior would be revisited for "simple" types (i.e., scalars) since it effectively disables strict null checking when implementing a function. I discovered the issue when I did something like this:

Sentry.init({
  beforeSend(event: Sentry.Event, hint: Sentry.Hint) { ... }
})

since TS didn't warn me that the hint param can be undefined.

But feel free to close if that discussion is already been had or out of scope.

@fatcerberus
Copy link

This is a very commonly encountered issue, for what it's worth.

If you're able to use strictFunctionTypes in your project, then you can declare your methods as fields if you want them to be checked contravariantly. It's even enough just to do that in the interface definition, object literals assigned to it can still use method syntax:

https://www.typescriptlang.org/play?ts=4.5.2#code/PTAEFkE9QYwewCYFNQHM5IM6gBZIE5IBQRAlgHYAuBAZgIYwoCqmBoA3kaN6OXQLZIAXKEyV8FVAG4iAXxIVq+eo1ABxQkiUcuaTZREAKTHQA2AV0p1KpOORFiJ5VABpQ51vgD8IlmwA+vOb8AEYEAJSgALwAfKLikjLyRPDkYqA0cPj8ZhpIWgQieQX40TrcqPrGbh6FoH74kZw8oCAt7dwAet2dutxtAMo4cOamCLxwlKBhoGamcADuSAgiAAYNoIHkwWGlgebkyDQUy6ughjT4cPzq+hF9rWCYw6Pj5JPTKHSYmKSofCFTChKHBQOtPGc8IQAHQPQiUcz4chggAk7EwslA-GgyDohHSaNq+GhfEEslWMhabQ6NNpXR6DN6VLAAHVSKZTKBKDgrgtZqB8AcbIJQAQrqUwjA6LV3J4sXRoAwEXNoDMDkcTggHm0svztqECLDuLIXHIZCk7Jg4EDofNUBcsjlTMUlNDKvlKIYAER4DlwL1udi8ATCUBegAq+DoADdSJgvaBZOFwjJUlabXaHdlcndie6tN7ffMvcmZEA

@DanielRosenwasser DanielRosenwasser added the Working as Intended The behavior described is the intended behavior; this is not a bug label Dec 3, 2021
@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' 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
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

5 participants