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

Disallow uninitialised property overrides #33423

Closed
wants to merge 18 commits into from

Conversation

sandersn
Copy link
Member

@sandersn sandersn commented Sep 13, 2019

This PR is part of the migration from [[Set]] semantics to [[Define]] semantics. #27644 tracks the complete migration. This is the third step.

Briefly, uninitialised property declarations that must now use new syntax when the property overrides a property in a base class. The new syntax is only allowed in this case and is an error elsewhere. That's because when Typescript supports [[Define]] semantics, it will start emitting uninitialised property declarations, where previously it did not. This will be a major breaking change:

class B {
  p: number
}
class C extends B {
  p: 256 | 1000 // use whatever value you need here; it
}

Previously this emitted

class B {}
class C extends B {}

When Typescript supports [[Define]] semantics, it will instead emit

class B {
  p
}
class C extends B {
  p
}

which will give both B and C and property p with the value undefined. (This is an error today with strictNullChecks: true.)

The new syntax will cause Typescript to emit the original JS output:

class B {
  p: number
}
class C extends B {
  declare p: 256 | 1000
}

This PR adds an error prompting the author to add the new syntax in order to avoid the breaking change. As you can see from the baselines, this error is pretty common. From my first run of our extended test suites, it looks pretty common in real code as well.

Note that the compiler can only check constructors for initialisation when strictNullChecks: true. I chose to be conservative and always issue the error for strictNullChecks: false.

Other ways to fix the error

  1. Make the overriding property abstract.
  2. Make the base property abstract.
  3. Give the overriding property an initialiser.
  4. Initialise the overriding property in the constructor -- but this only works with "strictNullChecks": true.

Notes

  1. Adding a postfix-! will not fix the error; ! is always erased, so, in the last example, p!: 256 | 1000 would still result in p === undefined for [[Define]] semantics.
  2. You can add declare to any uninitialised property, even if it's not an override. I previously had a check that prevented this, but I think an easy upgrade is valuable (you can almost write a regex for it).

To test this PR

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/44156/artifacts?artifactName=tgz&fileId=1E6A72F0C6E099FF6B046F9DBFDBA5D1CF45DD963781CEFBCFDBBDE15FD60F7C02&fileName=/typescript-3.7.0-insiders.20190916.tgz"
    }
}

Future work

The upcoming flag to change class field emit to [[Define]] semantics should disable this error.

This causes quite a few test breaks. We'll probably want to revert many
of them by switching to the upcoming `declare x: number` syntax.
@sandersn
Copy link
Member Author

@typescript-bot test this
@typescript-bot run dt
@typescript-bot user test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 13, 2019

Heya @sandersn, I've started to run the extended test suite on this PR at ac96739. You can monitor the build here. It should now contribute to this PR's status checks.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 13, 2019

Heya @sandersn, I've started to run the parallelized community code test suite on this PR at ac96739. You can monitor the build here. It should now contribute to this PR's status checks.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 13, 2019

Heya @sandersn, I've started to run the parallelized Definitely Typed test suite on this PR at ac96739. You can monitor the build here. It should now contribute to this PR's status checks.

@typescript-bot
Copy link
Collaborator

The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master.

@AlCalzone
Copy link
Contributor

Is there any way how I can test my code against this? From reading your recent PRs it seems that one of my libraries will be affected by the change.

Will update after checking out other branch for a minute
@sandersn
Copy link
Member Author

@typescript-bot pack this
@AlCalzone the code isn't quite done yet, but typescript-bot will produce a tgz on Azure that you can point your package.json to.

The extended tests look pretty grim, but at least this error will be pretty easy to fix.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 16, 2019

Heya @sandersn, I've started to run the tarball bundle task on this PR at ac96739. You can monitor the build here. It should now contribute to this PR's status checks.

@typescript-bot
Copy link
Collaborator

Hey @sandersn, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/44156/artifacts?artifactName=tgz&fileId=1E6A72F0C6E099FF6B046F9DBFDBA5D1CF45DD963781CEFBCFDBBDE15FD60F7C02&fileName=/typescript-3.7.0-insiders.20190916.tgz"
    }
}

and then running npm install.

Need to test properties initialised in constructor
@sandersn
Copy link
Member Author

@typescript-bot test this
@typescript-bot run dt
@typescript-bot user test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 16, 2019

Heya @sandersn, I've started to run the parallelized community code test suite on this PR at 8686242. You can monitor the build here. It should now contribute to this PR's status checks.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 16, 2019

Heya @sandersn, I've started to run the parallelized Definitely Typed test suite on this PR at 8686242. You can monitor the build here. It should now contribute to this PR's status checks.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 16, 2019

Heya @sandersn, I've started to run the extended test suite on this PR at 8686242. You can monitor the build here. It should now contribute to this PR's status checks.

@typescript-bot
Copy link
Collaborator

The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master.

@sandersn
Copy link
Member Author

@rkirov @mprobst @evmar You will probably want to take a look at this PR too. The errors are easier to fix compared to #33401.

@sandersn
Copy link
Member Author

The codefix is in -- @rbuckton I think this is ready for review now.

@@ -29583,6 +29603,9 @@ namespace ts {
}
const constructor = findConstructorDeclaration(node);
for (const member of node.members) {
if (getModifierFlags(member) & ModifierFlags.Ambient) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not require ! on declare x: number (since it is in fact not even allowed)

@ljharb
Copy link
Contributor

ljharb commented Sep 17, 2019

It seems useful to ship declare syntax ASAP, and encourage people to migrate to it, before separately shipping the Set → Define change. Is that an option?

@sandersn
Copy link
Member Author

@ljharb Our current plan is to ship the new syntax, the new errors, and the flag to switch to Define semantics at the same time. The flag will initially default to Set semantics, which also silences the errors. People can switch the flag on, fix errors, then switch it off again. Notably, the fixes for both errors are the same with both Set and Define semantics.

We'll discuss this at our design meeting on Friday so we should have a final decision then.

@ljharb
Copy link
Contributor

ljharb commented Sep 18, 2019

Mainly thinking that the declare syntax is at least explicit, and that you may want to hold off merging the rest until after the next TC39 meeting, in case the topic comes up. (not trying to imply anything will change about the proposal)

@sandersn
Copy link
Member Author

The errors in #33401 that disable accessor/property override mismatch are independently useful, although admittedly we haven't got many complaints from people confused by the current Set behaviour.

@sandersn
Copy link
Member Author

Note: this-property assignments are still incorrectly erroring in JS.

@littledan
Copy link

@ljharb I don't see anything on the October 2019 TC39 agenda about this topic, and the deadline for adding agenda items has passed. Class fields have been stable with Define semantics for the whole time they have been at Stage 3, since 2017, and these semantics have shipped unflagged in Firefox, Chrome, and Node.js, as well as other environments.

@ljharb
Copy link
Contributor

ljharb commented Sep 22, 2019

The deadline is for stage advancement; there’s no deadline for discussion topics, which is what i was alluding to.

@AlCalzone
Copy link
Contributor

@sandersn Surprisingly little errors (only 3) in my codebase. I guess I squashed most of them when I switched testing to Babel+Jest. Back then I switched from redefining the class properties to interface merging, i.e.

class BaseClass {
  property: number
}

interface SubClass {
  property: 1 | 2 | 3
}

class SubClass extends BaseClass {
  // ...implementation
}

which is the ugly brother of the define syntax that has been proposed.

@sandersn
Copy link
Member Author

This is now included in #33509. Thanks all for testing.

@AlCalzone we decided that interface merging is too esoteric a fix to recommend for the number of errors this causes. Also it's harder to write a codefix for.

@ljharb After our design meeting: #33509 has everything in one PR, and will keep the errors behind a flag while adding the new syntax for everyone to use.

@sandersn sandersn closed this Sep 23, 2019
@AlCalzone
Copy link
Contributor

@sandersn is define inside classes going to be in 3.7?

@sandersn
Copy link
Member Author

sandersn commented Oct 1, 2019

Yes. Well, declare is, I assume that's what you meant.

@AlCalzone
Copy link
Contributor

👍🏻

@jakebailey jakebailey deleted the disallow-uninitialised-property-override branch November 7, 2022 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants