Skip to content

Literal type initialization with inheritance #10484

Description

@jods4

TypeScript Version: nightly (2.1.0-dev.20160822)

Code

class A {
 protected x: "a" | "b";
}

class B extends A {
  protected x = "b";
}

Expected behavior: Should compile without error. Code seems intuitive to me.

Actual behavior: error TS2415: Class 'B' incorrectly extends base class. Type 'string' is not assignable to type '"a" | "b"'.

Activity

  1. sandersn commented on Aug 22, 2016

    @sandersn
    Member

    We tried this a while back, but failed to find a good solution. #6118 gives the result of that work.

    Proposals are welcome to fix this problem.

  2. sandersn commented on Aug 22, 2016

    @sandersn
    Member

    Duplicate of #3667. The best place to put a proposal is on #6118 if it's simple or in a new issue if it's not.

  3. jods4 commented on Aug 23, 2016

    @jods4
    Author

    Nathan Shively-Sanders (@sandersn) I read your comment about RWC. It seems the main issue is lack of variance contract for members of classes. Typing a property with a narrower type in a derived class is OK if your code only reads from the base class but possibly broken if you also write. And there is no way to know this.

    In a greenfield there are easy way to fix this, but of course they break existing code 👎 .
    Given the constraints, I don't see a good easy solution to make it work + make it backward compatible.

    Only thing that comes to mind is a compiler flag 😦
    The benefits are important though. Here are some annoyances that I encounter on a daily basis:

    • The example above, where assigning a string to a literal type fails.
    • Typing empty arrays! In a derived class or interface or literal parameter, xs = [] fails and is annoying as hell.
    • Implicitly typed lambda parameters!
    • Bugs because overrides are not properly checked at all.

    Some of those are so frequent that I would love even a partial fix, e.g. the empty array initializer and implicitely typed lambdas. Those should be doable with backward compatibility, I think?

  4. sandersn commented on Aug 23, 2016

    @sandersn
    Member

    At this point we're starting to consider patchwork fixes instead of a general fix. Daniel Rosenwasser (@DanielRosenwasser) and I were going to get together soon to brainstorm some way to make partial fixes easy to explain if not completely consistent.

    In order of likelihood, this is what I'm thinking we would be able to solve:

    1. Overrides without type annotations — otherwise you don't invoke contextual typing. (Modifying contextual typing to work with partial annotations is another big, separate problem complicated primarily by the interaction of contextual typing of lambdas while doing type parameter inference).
    2. Empty arrays assigned to an overridden property.
    3. Un-annotated lambdas assigned to an overridden property. This one seems like the same thing as overridden methods, but they are treated differently by the compiler in some ways.
    4. Typing string literals/null/undefined. There is a long tail of problems that are harder to solve and also have less value. (a property p = null gives you p: any which degrades checking but is otherwise benign).

    Unfortunately, the deeper you go on the list, the more of a random bag of patches it becomes. I'm not sure of a solution for that either.

  5. jods4 commented on Aug 23, 2016

    @jods4
    Author

    Nathan Shively-Sanders (@sandersn) It is a good list!
    I think all my main offenders are on it, thanks! 👏

  6. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions