You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This file contains hidden or 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
This PR implements a new --strictPropertyInitialization compiler option to guard against uninitialized properties in class instances. Strict property initialization checking verifies that each instance property declared in a class either (a) has a type that includes undefined or (b) has an explicit initializer or an assignment to the property in the constructor. The --strictPropertyInitialization option has no effect unless --strictNullChecks is also enabled.
The --strictPropertyInitialization switch is part of the --strict family of switches, meaning that it defaults to on in --strict mode. This PR is therefore a breaking change only in --strict mode.
In this example
// Compile with --strictclassTest{a: number;// Error, property not initializedb?: string;// Ok, type includes undefined}
an error is reported on property a because its type doesn't include undefined and the property declaration doesn't specify an initializer or assign a value to the property in the constructor. The error can be fixed by changing the type of a to include undefined, by specifying an initial value for a, or by assigning a value to a in the constructor. For example, this is ok:
// Compile with --strictclassTest{a: number=0;// Okb?: string;}
A strict property initialization check is satisfied by assignments in the constructor only when all possible code paths include assignments to the particular property.
// Compile with --strictclassTest{a: number;constructor(startAtZero: boolean){if(startAtZero){this.a=0;}else{this.a=1;}}}
In the example above, an error would occur if either of the if statement branches omitted the assignment to this.a.
Strict property initialization checks guard against observing uninitialized properties in the constructor body. For example:
// Compile with --strictclassTest{a: number;constructor(){letx=this.a;// Errorthis.a=0;}}
Strict property initialization checks also guard against observing uninitialized properties after the constructor returns. However, it is possible to observe uninitialized properties in methods that are called from the constructor (or from a base constructor). For example:
// Compile with --strictclassTest{a: number;constructor(){this.foo();this.a=0;}foo(){// Uninitialized value of 'a' observable here}}
Strict property initialization checks only apply to properties that are declared with proper identifiers. It does not apply to properties with computed names or properties with names specified as string or numeric literals (this is analogous to how control flow analysis isn't applied to properties accessed using obj["foo"] syntax).
// Compile with --strictclassTest{a: number;// Error, not initialized"hello world": number;// No check}
In cases where the properties of a class instance are initialized by external means (such as dependency injection) or where it is known that an initialization method is always called before other methods, strict property initialization checks can be suppressed by including definite assignment assertions (#20166) in the declarations of the properties.
// Compile with --strictclassC{a!: number;// Use ! to suppress errorb!: string;// Use ! to suppress errorinitialize(){this.a=0;this.b="hello";}}
Alternatively, // @ts-ignore comments can be used to suppress the errors, or the code can simply be compiled without the --strictPropertyInitialization option.
Note that strict property initialization checks only guard against observing uninitialized properties after the constructor returns. It is still possible to observe uninitialized properties in methods that are called from the constructor (or from a base constructor).
Is there a reason TS can't check if the called functions access the uninitialized value, then report the error in the constructor?
@kingdaro we only do control flow analysis across function bounds for immediately invoked function expressions; to do otherwise would be error-prone in the presence of aliasing, so we conservatively avoid doing so.
What about initializing helpers?
Suppose that constructor calls a method that complete the initialization (e.g. the developer has put this initialization in a separate method because it should be called also after object construction).
What you propose to handle this case?
Also, does this PR handle cases where error is thrown before all fields initialised?
It is fine to throw an exception before all properties are initialized, and yes you can expose an object that isn't fully initialized to the outside (just like you can call methods before the object is fully initialized).
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
None yet
7 participants
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 implements a new
--strictPropertyInitialization
compiler option to guard against uninitialized properties in class instances. Strict property initialization checking verifies that each instance property declared in a class either (a) has a type that includesundefined
or (b) has an explicit initializer or an assignment to the property in the constructor. The--strictPropertyInitialization
option has no effect unless--strictNullChecks
is also enabled.The
--strictPropertyInitialization
switch is part of the--strict
family of switches, meaning that it defaults to on in--strict
mode. This PR is therefore a breaking change only in--strict
mode.In this example
an error is reported on property
a
because its type doesn't includeundefined
and the property declaration doesn't specify an initializer or assign a value to the property in the constructor. The error can be fixed by changing the type ofa
to includeundefined
, by specifying an initial value fora
, or by assigning a value toa
in the constructor. For example, this is ok:A strict property initialization check is satisfied by assignments in the constructor only when all possible code paths include assignments to the particular property.
In the example above, an error would occur if either of the
if
statement branches omitted the assignment tothis.a
.Strict property initialization checks guard against observing uninitialized properties in the constructor body. For example:
Strict property initialization checks also guard against observing uninitialized properties after the constructor returns. However, it is possible to observe uninitialized properties in methods that are called from the constructor (or from a base constructor). For example:
Strict property initialization checks only apply to properties that are declared with proper identifiers. It does not apply to properties with computed names or properties with names specified as string or numeric literals (this is analogous to how control flow analysis isn't applied to properties accessed using
obj["foo"]
syntax).In cases where the properties of a class instance are initialized by external means (such as dependency injection) or where it is known that an initialization method is always called before other methods, strict property initialization checks can be suppressed by including definite assignment assertions (#20166) in the declarations of the properties.
Alternatively,
// @ts-ignore
comments can be used to suppress the errors, or the code can simply be compiled without the--strictPropertyInitialization
option.Fixes #8476.