Skip to content

Allowing subclasses to narrow types from the superclass breaks substitutability #42750

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
bicknellr opened this issue Feb 11, 2021 · 7 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@bicknellr
Copy link

Bug Report

🔎 Search Terms

  • liskov
  • liskov substitution
  • substitution
  • subclass narrow
  • subclass substitution

🕗 Version & Regression Information

⏯ Playground Link

💻 Code

class A {
  f(p: string | number) {
    console.log('A', p);
  }
}

class B extends A {
  f(p: string) {
    console.log('B', p, p.charCodeAt(0));
  }
}

const arrayOfA: Array<A> = [new A(), new B()];
for (const item of arrayOfA) {
  item.f(123);
}

Also:

class A {
  static f(p: string | number) {
    console.log('A', p);
  }
}

class B extends A {
  static f(p: string) {
    console.log('B', p, p.charCodeAt(0));
  }
}

const arrayOfA: Array<typeof A> = [A, B];
for (const item of arrayOfA) {
  item.f(123);
}

🙁 Actual behavior

TypeScript compiles this code with no error. A runtime error is thrown when B.prototype.f (or B.f) attempts to call .charCodeAt of 123.

🙂 Expected behavior

TypeScript should fail to compile this code, with an error explaining that B may not override f with a function that narrows the type of parameter p.

@bicknellr
Copy link
Author

@justinfagnani pointed me to a couple related issues around the static half of this: #5863 #4628

@RyanCavanaugh
Copy link
Member

This is exactly method bivariance.

@bicknellr
Copy link
Author

I'm not experienced enough with type systems to know if you're using the phrase "method bivariance" to refer to the same thing as the "function parameter bivariance" from the FAQ section or if you're calling out that this is different because this involves a method of a class, rather than some arbitrary function. Do you think this is a valid issue or is this expected behavior?

@RyanCavanaugh
Copy link
Member

This is the expected behavior; the widespread prevalence of classes with methods that behave this way (e.g. the DOM event methods, classes with overly strict "compareTo" methods, etc) is why we still have function/method parameter bivariance.

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Feb 12, 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.

@Woodz
Copy link

Woodz commented Apr 22, 2024

I strongly recommend that this issue is re-opened and reviewed. This breaks typing of many OOP design patterns (e.g. abstract factory, etc), causing errors that can only be caught at runtime

@RyanCavanaugh
Copy link
Member

We're aware of what the implications of this are. See also https://github.com/microsoft/TypeScript/wiki/FAQ#this-is-closed-but-should-be-open-or-vice-versa

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

4 participants