Skip to content

strictPropertyInitialization with subclasses that refine the types of properties #20911

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

Open
rictic opened this issue Dec 28, 2017 · 4 comments
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@rictic
Copy link
Contributor

rictic commented Dec 28, 2017

TypeScript Version: 2.7.0-dev.20171226

Code

class A {
  x: {y: string} = {y: 'y'};
}

class B extends A {
  x: {y: string, z?: number};
}

Expected behavior:
I expected this not to warn because A#x is assignable to B#x.

Actual behavior:

index.ts(6,3): error TS2564: Property 'x' has no initializer and is not definitely assigned in the constructor.

@DanielRosenwasser DanielRosenwasser added the Bug A bug in TypeScript label Dec 28, 2017
@DanielRosenwasser
Copy link
Member

Seems undesirable. CC @ahejlsberg.

@Jessidhia
Copy link

Jessidhia commented Dec 28, 2017

Note: this is not the case in TypeScript's implementation, but in the current stage-3 class properties, x in B is a completely different own prop that has the same name.

This does mean that, in a stage-3 compliant implementation, (new B).x will be a new own prop with the value undefined since it lacks an initializer. It'd have to be initialized with = this.x in order to copy the value that comes from the superclass constructor before it is overridden with the new property definition.

@rictic
Copy link
Contributor Author

rictic commented Dec 28, 2017

@Kovensky Huh! Reading through: https://tc39.github.io/proposal-class-fields/#initialize-public-instance-fields it seems like [[Fields]] will have two entries, first A's x then B's x, and CreateDataPropertyOrThrow will assign undefined when evaluating B's x.

What the code is trying to do is declare the type of the existing field rather than declaring the field itself. This works today and expresses that intent pretty well. Typescript even gives an error if the declared type of B#x is not assignable to A#x:

class A {
  x: {y: string} = {y: 'y'};
}

class B extends A {
}

interface B {
  x: {y: string, z?: number};
}

I'd like someone else to double check my reasoning here, but if all of the above is correct then I think this issue can be closed as working correctly.

@thw0rted
Copy link

Ryan posted a very helpful suggestion over in #35831. If you're using at least TS 3.7, there's a declare modifier for class properties that lets you narrow the type of a property in a subclass without emitting any runtime initializer code, and it also makes strictPropertyInitialization happy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

6 participants