In TypeScript, if strict null checking is enabled, I would expect the compiler to prevent me from assigning null
or undefined
values to a variable unless it admits null
.
However, array access seems to allow circumventing this check.
Example:
let a: string[] = ["Hello"];
let s: string;
// 1) this produces an error, as expected
s = undefined
// 2) s is undefined here, too, but no error
s = a[3];
console.log(s);
Runnable version on the TypeScript Playground (Note: "strict null checking" must be enabled in the "Options" dialog).
What is going on here?
Found it :-).
tl;dr: It is a deliberate omission. Array access is very common in TypeScript code, and forcing a null/undefined check for every access was considered too cumbersome for developers.
Note that since TypeScript 4.1 this is configurable with the compiler option noUncheckedIndexedAccess
. See Klaster_1's answer for details.
The issue has been raised several times in discussions:
The comment on PR 7140 has a nice rationale from Anders Hejlsberg (one of the core developers):
Indexing simply produces a value of the type declared in the matching index signature. Even if it is technically more correct, it would simply be too painful if we automatically added
undefined
to the type of every indexing operation.For example, every array element access would have to be accompanied by a non-null guard or a
!
assertion. I think that would become hugely irritating.
Personal comment: I think this is a reasonable decision. The problem is inherent with arrays - I don't think it's possible to prove (for the compiler) whether a given position is defined, since you can use calculated positions. So any warning would produce many false positives, and most devs would switch it off anyway.
Essentially, arrays provide more freedom to the developer than the compiler can check. If you want proper checking, the only solution I can see is to avoid direct array access, and use other data structures that provide more protection.
TypeScript 4.1 introduced a new compiler option - noUncheckedIndexedAccess. Among other things, it adds undefined
to array index access type.
Consider the following snippet (TS playground):
const items = [1,2,3]
console.log(items[1]?.toString(10))
console.log(items[2].toString(10))
Without noUncheckedIndexedAccess
, items[2].toString(10)
will be deemed valid, and invalid when the option is on. The items[1]?.toString(10)
will be valid when the option is on, just like in comment by Alex Neth in the older answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With