-
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
Uninitialized variables work around strictNullChecks (follow-up to #13884) #23305
Comments
For the time being you could use a lint rule. I have a written a rule to detect variables that are never assigned, which would cover the simple examples you provided above: The rule |
The addition of |
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 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 |
That sounds like a fair approach, but it seems that it would only get us half way there: let x: number;
function use() {
x.toString(); // Usage, therefore needs an assignment at some point.
}
function assign() {
x = 5; // Assignment, so all is well.
}
use(); // Oops, this is out of order, but it's too late now.
assign(); Maybe this is impossible to enforce without complete flow analysis. |
Inlining the effects and requirements of every function call is out of scope in terms of our current flow control analysis architecture. Nothing we can do will get us 100%, e.g. // Does what it says
declare function callInRandomOrder(fn1: () => void, fn2: () => void);
let x: number;
function use() {
x.toString(); // Usage, therefore needs an assignment at some point.
}
function assign() {
x = 5; // Assignment, so all is well.
}
callInRandomOrder(use, assign); |
Really excited about the progress here and openness from the team about addressing this thank you! ❤️ As we've established, the usage before initialization is an undecidable problem and doing any sort of flow analysis will have its limits. Is there opposition from the typescript team to also adding the proposed |
@RyanCavanaugh Right - in your example, since it cannot be proven that Totally understood that this is impossible with the current control flow analysis architecture. Just discussing ideals. In the meantime, what you proposed would certainly be a welcome addition. |
You can log another suggestion for |
Wait, how is @RyanCavanaugh's suggestion different from |
If I understood his suggestion and call for PRs correctly, the following code would compile fine let foo: Foo
foo = new Foo()
foo.bar() But with |
Ah. I would argue that there shouldn't be an error in your example. It can be proved that |
@agopshi sure in my example, I don't really care about the error; it was a trivial example. It's just that previous attempts have been shot down because the generic problem is undecidable, and I recognize that fact. It'd be nice to have a surefire way to express that |
There's surely a TSLint rule that enforces initializations in |
@RyanCavanaugh the TSLint rule is actually the opposite :) it seems I will lose this debate yet again and perhaps I'd be better off adding a PR for a rule option to tslint instead, but I still hold that this seems like a fundamental responsibility of a type system. |
I'm not following why this continuously was shot down. It seems clear that this should be handled by a compiler, not by a lint rule. The language sets uninitialized |
While I appreciate the MS Paint work, I find it tough to have followed 3 different conversation on this topic and even just within this thread seen it flip to suggesting this is better as a lint rule. If there is not fundamental agreement that an uninitialized |
I would actually rather have the simpler |
I suppose the workaround for now is to use eslint to forbid uninitialized variables altogether (i.e., turn on I do like the idea of an optional TS flag that gives |
It seems a bit weird to ask users to use separate program (tslint) to avoid shortcoming in compiler. So instead of |
It should be an error even if it has |
This 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 analogousstrictLocalInitialization
.TypeScript Version: 2.8
Search Terms: uninitialized local variables, strict local variables, strictLocalInitialization, strictPropertyInitialization, strictNullChecks
Code
Slightly more interesting:
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:
#13884, #9998
Understandably, this may not be as simple as
strictPropertyInitialization
, but I feel it needs a discussion none the less. ThefooFactory()
example above is analogous to thisFoo
class, which correctly emits a compile-time error understrictPropertyInitialization
:The text was updated successfully, but these errors were encountered: