Uninitialized variables work around strictNullChecks (follow-up to #13884)
See original GitHub issueThis is a follow-up to #13884, where @RyanCavanaugh asked me to open a new issue.
tl;dr: Now that there is strictPropertyInitialization
, it seems that there should also be an analogous strictLocalInitialization
.
TypeScript Version: 2.8
Search Terms: uninitialized local variables, strict local variables, strictLocalInitialization, strictPropertyInitialization, strictNullChecks
Code
let x: number[]; // No definite assignment assertion.
// x.toString(); // Compile-time error. Yay!
function foo() {
x.toString(); // No compile-time error, but throws an exception. :(
}
foo();
Slightly more interesting:
function fooFactory() {
let x: number; // Inaccessible from anywhere but `foo()` below.
return {
foo() {
// Guaranteed to throw an exception since `x` can't change.
return x.toString();
}
};
}
Expected behavior:
If an imaginary --strictLocalInitialization
flag is enabled, these snippets should each result in a compile-time error.
To resolve such an error, one would need to:
A) Initialize the variable in a way that the TypeScript compiler understands (e.g. immediately, or before referencing it in a closure).
-OR-
B) Use a definite assignment assertion. It seems that this issue would come up naturally when discussing definite assignment assertions for local variables. Instead, the example provided in the blog feels contrived and wouldn’t be a problem if it weren’t for control flow analysis limitations.
-OR-
C) Explicitly mark the variable as potentially undefined
, forcing callers to acknowledge that it may not be set.
Actual behavior:
No compile-time errors, but obvious run-time errors.
Playground Link:
Link (enable strictNullChecks
)
Related Issues:
Understandably, this may not be as simple as strictPropertyInitialization
, but I feel it needs a discussion none the less. The fooFactory()
example above is analogous to this Foo
class, which correctly emits a compile-time error under strictPropertyInitialization
:
class Foo {
private x: number; // Compile-time error!
foo() {
// `x` isn't initialized! But it's OK, compile-time error above.
return this.x.toString();
}
}
Issue Analytics
- State:
- Created 5 years ago
- Reactions:15
- Comments:18 (6 by maintainers)
Top GitHub Comments
Accepting PRs. The rule here is that any variable that is read but never written to is an error if the variable doesn’t have
undefined
in its type.Implementation note - this needs to be done not as a separate pass. Re-use the existing logic that finds unused locals and refactor it to provide read/write information instead of just read.
No new flag since this should only ever catch true errors – this will be the default behavior for all
strictNullChecks
compilations.I would actually rather have the simpler
--strictLocalInitialization
option that flags any uninitialized declaration which does not have undefined as an allowable type, than a control-flow based feature that allows a subset of such constructs where assignment before use can be proven. And even more so if the control-flow based feature is going to permit some constructs where there is an assignment but it can’t be proven to occur before use – I think the majority of bugs people hope to catch with strictness like this would fall in this category.