Infer const-ness of local let bindings that aren't assigned to
See original GitHub issueSearch Terms
let const infer assignment
Suggestion
In some cases, type narrowing for const
bindings is more effective than for let
bindings. Specifically, using such bindings from a function that closes over them will keep the narrowed type, which doesn’t happen for let
binding, presumably on the assumption that those might be mutated at any time.
This suggests to treat local let
bindings that are never assigned to (which can be determined by a rather straightforward static check on the syntax tree) like const
bindings for this purpose.
(Interestingly, something like this seems to already exist for function parameters.)
Use Cases
Though some people have adopted a coding style where locals are declared with const
unless assigned to, there’s also a good argument to be made that that is not a very good use of brain cycles and just using let
for all locals is fine. This proposal would improve type inference for people using the let
style.
Examples
function foo() {
let v: number[] | null = Math.random() < 0.5 ? [1] : null
if (Array.isArray(v)) setTimeout(() => console.log(v.length), 100)
}
The compiler complains that v
is possibly null. If the binding is changed to const
the problem goes away. If v
is made a function parameter it also doesn’t occur. (But comes back when a v = null
statement is added, suggesting a check like the one suggested here is being done in that case.)
Checklist
My suggestion meets these guidelines:
- This wouldn’t be a breaking change in existing TypeScript/JavaScript code
- This wouldn’t change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn’t a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript’s Design Goals.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:9 (5 by maintainers)
If there is a scope resolution phase or similar, where identifiers are associated with declarations, and you have a single bit free in the flags for declaration items, I believe this could be implemented by allocating an is-assigned-to bit in the declaration and setting that when coming across an assignment. This is always file-local, for local variables. But I may be missing something.
I agree, this wouldn’t necessarily be a breaking change because we’d still widen types when inferring the declared type of a
let
variable. My concerns are more around performance impact. It’s not a cheap check. Also, previous requests for this feature generally asked for analysis that no assignments occur following creation of the function closure, as opposed to no assignments at all other than the initialization of the variable. That is a much harder problem to solve, and not one that we could solve with guaranteed correctness.