-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Add useDefineForClassFields flag for Set -> Define property declaration #33509
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Unless the base property or accessor is abstract
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.
1. Don't error when overriding properties from interfaces. 2. Fix error when overriding methods with other things. This had no tests so I assume that the code was always dead and never worked.
Will update after checking out other branch for a minute
Need to test properties initialised in constructor
And simplify redundant parts of check.
…/add-property-define-flag
…property-define-flag
@typescript-bot pack this |
sandersn
added a commit
that referenced
this pull request
Oct 15, 2019
4 tasks
There is nothing about it in the RC release notes. Will it be mentioned in the final release notes or it will be postponed to the next release? |
when useDefineForClassFields = false
when useDefineForClassFields = true
|
OK |
5 tasks
This was referenced Sep 8, 2020
1 task
moz-v2v-gh
pushed a commit
to mozilla/gecko-dev
that referenced
this pull request
Dec 22, 2023
…nce r=robwu These fall under 5 main categories: 1) Declare and/or initialize all class fiels in the constructor. (general good practise) 2) Use real getters and redefineGetter instead of defineLazyGetter. (also keeps related code closer together) 3) When subclassing, don't override class fields with getters (or vice versa). microsoft/TypeScript#33509 4) Declare and assign object literals at the same time, not separatelly. (don't use `let foo;` at the top of the file, use `var foo = {`) 5) Don't re-use local variables unnecesarily with different types. (general good practise, local variables are "free") Differential Revision: https://phabricator.services.mozilla.com/D196386
ErichDonGubler
pushed a commit
to erichdongubler-mozilla/firefox
that referenced
this pull request
Dec 22, 2023
…nce r=robwu These fall under 5 main categories: 1) Declare and/or initialize all class fiels in the constructor. (general good practise) 2) Use real getters and redefineGetter instead of defineLazyGetter. (also keeps related code closer together) 3) When subclassing, don't override class fields with getters (or vice versa). microsoft/TypeScript#33509 4) Declare and assign object literals at the same time, not separatelly. (don't use `let foo;` at the top of the file, use `var foo = {`) 5) Don't re-use local variables unnecesarily with different types. (general good practise, local variables are "free") Differential Revision: https://phabricator.services.mozilla.com/D196386
gecko-dev-updater
pushed a commit
to marco-c/gecko-dev-wordified-and-comments-removed
that referenced
this pull request
Jan 1, 2024
…nce r=robwu These fall under 5 main categories: 1) Declare and/or initialize all class fiels in the constructor. (general good practise) 2) Use real getters and redefineGetter instead of defineLazyGetter. (also keeps related code closer together) 3) When subclassing, don't override class fields with getters (or vice versa). microsoft/TypeScript#33509 4) Declare and assign object literals at the same time, not separatelly. (don't use `let foo;` at the top of the file, use `var foo = {`) 5) Don't re-use local variables unnecesarily with different types. (general good practise, local variables are "free") Differential Revision: https://phabricator.services.mozilla.com/D196386 UltraBlame original commit: 8e768446e17cc306729e3b0f705b0285c69321cf
gecko-dev-updater
pushed a commit
to marco-c/gecko-dev-wordified
that referenced
this pull request
Jan 1, 2024
…nce r=robwu These fall under 5 main categories: 1) Declare and/or initialize all class fiels in the constructor. (general good practise) 2) Use real getters and redefineGetter instead of defineLazyGetter. (also keeps related code closer together) 3) When subclassing, don't override class fields with getters (or vice versa). microsoft/TypeScript#33509 4) Declare and assign object literals at the same time, not separatelly. (don't use `let foo;` at the top of the file, use `var foo = {`) 5) Don't re-use local variables unnecesarily with different types. (general good practise, local variables are "free") Differential Revision: https://phabricator.services.mozilla.com/D196386 UltraBlame original commit: 8e768446e17cc306729e3b0f705b0285c69321cf
gecko-dev-updater
pushed a commit
to marco-c/gecko-dev-comments-removed
that referenced
this pull request
Jan 1, 2024
…nce r=robwu These fall under 5 main categories: 1) Declare and/or initialize all class fiels in the constructor. (general good practise) 2) Use real getters and redefineGetter instead of defineLazyGetter. (also keeps related code closer together) 3) When subclassing, don't override class fields with getters (or vice versa). microsoft/TypeScript#33509 4) Declare and assign object literals at the same time, not separatelly. (don't use `let foo;` at the top of the file, use `var foo = {`) 5) Don't re-use local variables unnecesarily with different types. (general good practise, local variables are "free") Differential Revision: https://phabricator.services.mozilla.com/D196386 UltraBlame original commit: 8e768446e17cc306729e3b0f705b0285c69321cf
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR is part of the migration from [[Set]] semantics to [[Define]] semantics. #27644 tracks the complete migration. This PR includes steps 1-4 for Typescript 3.7:
class C extends B { declare x: number }
declare
where needed.useDefineForClassFields
."useDefineForClassFields": false
:"useDefineForClassFields": true
:"useDefineForClassFields"
defaults tofalse
Tasks from the design meeting:
Accessors may not override properties and vice versa
Briefly, properties can now only override properties, and accessors can only override accessors. The tests come from the user-submitted horrorshow parade in #27644. Thanks to all who contributed there.
Exceptions
Motivation
Accessors that override properties have always been error-prone with [[Set]] semantics, so these new errors are useful with [[Set]] or [[Define]] semantics. For example, base-class properties call derived accessors, which may be unexpected. The code below prints
2
, the derived value, but it also printsset 1
. That's because the basep = 1
calls derived accessorset p
.In fact, if the derived class attempts to make
p
readonly by leaving offset p
, the code crashes with"Cannot set property p of #<D> which has only a getter."
This should always have been an error. However, because we haven’t given errors before, and because fixing the errors is likely to be difficult, we probably would not have added them otherwise, or added them in so many cases. Here are some examples of code that will be tricky to change:CleverBase
is a framework class that intends to cache or transform the value provided bySimple
.Simple
is written by a framework user who just knows that they need toextend CleverBase
and provide some configuration properties. This pattern no longer works with [[Define]] semantics. I believe the Angular 2 failure I found below is similar to this case.This code is the same as the first example — accessors override a property — but the intent is different: to ignore the base's property. With [[Set]] semantics, it's possible to work around the initial set from the base, but with [[Define]] semantics, the base property will override the derived accessors. Sometimes a derived accessor can be replaced with a property, but often the accessor needs to run additional code to work around base class limitations. In this case, the fix is not simple. I saw this in a couple of Microsoft apps, and in one place it had a comment "has to be a getter so overriding the base class works correctly".
How to fix the errors
Property overrides accessor
SimpleUser will have an error on the property declaration
p
. The fix is to move it into the constructor as an assignment:Since
CleverBase
declaresp
, there's no need forSimpleUser
to do so.Accessor overrides property
SmartDerived will have an error on the get/set declarations for
p
. The fix is to move them into the constructor as anObject.defineProperty
:This doesn't have exactly the same semantics; the base never calls the derived setter for
p
. If the original setter does have skip-initial-set code to work around the current weird Typescript semantics, that code will need to be removed. However, all the real examples I saw were just using accessors to make the property readonly, so they could instead use a readonly property.Uninitialised property declarations may not override properties
Briefly, uninitialised property declarations that must now use new syntax when the property overrides a property in a base class. That's because when Typescript supports [[Define]] semantics, previously uninitialised property declarations will instead have an initial value of
undefined
. This will be a major breaking change:Previously this emitted
When Typescript supports [[Define]] semantics, it will instead emit
which will give both
B
andC
and propertyp
with the valueundefined
. (This is an error today withstrictNullChecks: true
.)The new syntax will cause Typescript to emit the original JS output:
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 forstrictNullChecks: false
.Other ways to fix the error
"strictNullChecks": true
.Notes
!
is always erased, so, in the last example,p!: 256 | 1000
would still result inp === undefined
for [[Define]] semantics.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).