Repository navigation
Description
Update: fixed by --noUncheckedIndexedAccess in TypeScript 4.1
Update: for my latest proposal see comment #13778 (comment)
With strictNullChecks enabled, TypeScript does not include undefined in index signatures (e.g. on an object or array). This is a well known caveat and discussed in several issues, namely #9235, #13161, #12287, and #7140 (comment).
Example:
const xs: number[] = [1,2,3];
xs[100] // number, even with strictNullChecksHowever, it appears from reading the above issues that many TypeScript users wish this wasn't the case. Granted, if index signatures did include undefined, code will likely require much more guarding, but—for some—this is an acceptable trade off for increased type safety.
Example of index signatures including undefined:
const xs: number[] = [1,2,3];
xs[100] // number | undefinedI would like to know whether this behaviour could be considered as an extra compiler option on top of strictNullChecks. This way, we are able to satisfy all groups of users: those who want strict null checks with or without undefined in their index signatures.
Activity
mhegazy commentedon Feb 1, 2017
With the exception of strictNullChecks, we do not have flags that change the type system behavior. flags usually enable/disable error reporting.
you can always have a custom version of the library that defines all indexers with
| undefined. should work as expected.OliverJAsh commentedon Feb 1, 2017
Mohamed Hegazy (@mhegazy) That's an interesting idea. Any guidance on how to override the type signatures for array/object?
gcnew commentedon Feb 1, 2017
There is
interface Array<T>inlib.d.ts. I searched by the regexp\[\w+: (string|number)\]to find other indexing signatures as well.OliverJAsh commentedon Feb 2, 2017
Interesting, so I tried this:
Any ideas?
mhegazy commentedon Feb 2, 2017
copy
lib.d.tslocally, saylib.strict.d.ts, change the index signature to[n: number]: T | undefined;, include the file in your compilation. you should see the intended effect.OliverJAsh commentedon Feb 2, 2017
Cool, thanks for that.
The issue with the suggested fix here is it requires forking and maintaining a separate
libfile.I wonder if this feature is demanded enough to warrant some sort of option out of the box.
On a side note, it's interesting that the type signature for the
getmethod on ES6 collections (Map/Set) returnsT | undefinedwhenArray/Objectindex signatures do not.mhegazy commentedon Feb 2, 2017
this is a conscious decision. it would be very annoying for this code to be an error:
and it would be unreasonable to ask every user to use
!. or to writeFor map this is not the case generally.
Similarly for your types, you can specify
| undefinedon all your index signatures, and you will get the expected behavior. but forArrayit is not reasonable. you are welcome to fork the library and make whatever changes you need to do, but we have no plans to change the declaration in the standard library at this point.I do not think adding a flag to change the shape of a declaration is something we would do.
Strate commentedon Feb 3, 2017
Mohamed Hegazy (@mhegazy) but for arrays with holes
a[i]is actually possibly undefined:Output is:
RyanCavanaugh commentedon Mar 14, 2017
We remain quite skeptical that anyone would get any benefit from this flag in practice. Maps and maplike things can already opt in to
| undefinedat their definition sites, and enforcing EULA-like behavior on array access doesn't seem like a win. We'd likely need to substantially improve CFA and type guards to make this palatable.If someone wants to modify their lib.d.ts and fix all the downstream breaks in their own code and show what the overall diff looks like to show that this has some value proposition, we're open to that data. Alternatively if lots of people are really excited to use postfix
!more but don't yet have ample opportunities to do so, this flag would be an option.DanTup commentedon Mar 15, 2017
Isn't one of the goals of TypeScript to allow errors to be caught at "compile" time rather than rely on the user to remember/know to do something specific? This seems to go against that goal; requiring the user to do something in order to avoid crashes. The same could be said for many other features; they're not needed if the developer always does x. The goal of TypeScript is (presumably) to make the job easier and eliminate these things.
I came across this bug because I was enabling
strictNullCheckson existing code and I already had a comparison so I got the error. If I'd been writing brand new code I probably wouldn't have realised the issue here (the type system was telling me I always always getting a value) and ended up with a runtime failure. Relying on TS developers to remember (or worse, even know) that they're supposed to be declaring all their maps with| undefinedfeels like TypeScript is failing to do what people actually want it for.kitsonk commentedon Mar 15, 2017
Actually the goal is:
What is being discussed here the likelyhood of an error (low in the opinion of the TypeScript team) and the common productive usability of the language. Some of the early change to CFA have been to be less alarmist or improve the CFA analysis to more intelligently determine these things.
I think the question from the TypeScript team is that instead of arguing the strictly correctness of it, to provide examples of where this sort of strictness, in common usage would actually identify an error that should be guarded against.
234 remaining items
ericbf commentedon Sep 21, 2020
I'd say that's a bug. Basically if
keyof Typeonly includes string/number literal types (as opposed to actually includingstringornumber), thenType[Key]whereKey extends keyof Typeshould not includeundefined, I'd think.jcalz commentedon Oct 23, 2020
cross-linking to #13195, which also looks at differences and similarities between "there's no property here" and "there is a property here but it's
undefined"RyanCavanaugh commentedon Nov 20, 2020
Tracking some issues related to design limitations here
forloop should be OKlengthchecks should allow further destructuringrubenlg commentedon Nov 25, 2020
FYI: I caught two bugs in my codebase thanks to this flag, so big thank you!
It's a bit annoying that checking
arr.length > 0is not sufficient to guardarr[0], but that's a minor inconvenience (I can rewrite the check to make tsc happy) compared to the extra safety, IMO.Timmmm commentedon Nov 26, 2020
Rubén López (@rubenlg) I agree about
arr.length. For just checking the first element I've rewritten our code likeBut there are a few places where we do
if (arr.length > 2)or more, which are a bit awkward. However I don't think the checking.lengthwould be totally type safe anyway since you can just modify it:Prints
undefined.ericbf commentedon Nov 26, 2020
That would essentially be a sparse array, which is out of scope for this sort of thing. There's always non-standard or sneaky things that could be done to get around the type checker.
MartinJohns commentedon Nov 26, 2020
There's always a way to invalidate the previous assumption, and that's the problem. Analyzing every possible execution path would be way too costly.
Document that index signatures do not include undefined
Perfectoff commentedon May 27, 2021
I think the best solution would be to throw an exception when trying to access a non-existent element. This is in line with normal programming practice in statically typed languages. And if you want to get a nullable value without throwing an exception, then a method like "tryGet" is used.
As I see, this behavior can be implemented by replacing the code during the compilation to the javascript file, like this:
object [index] --> (object[index] ?? throw ("index out of range"))
object.tryGet [index] --> object[index]
And, accordingly, add the tryGet method to the Array, Map and other interfaces with indexer.
rubenlg commentedon May 27, 2021
Perfectoff As a principle, TypeScript never changes the runtime behavior of JavaScript code:
https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html#runtime-behavior
What you are proposing are run-time assertions, and that could be implemented as a separate library.
thw0rted commentedon May 28, 2021
It seems to me that Perfectoff just wants to use a language that isn't Javascript. JS arrays have never had a concept of "out of bounds", Arrays have always been sparse, and Objects have always allowed arbitrary indexing. That's the whole reason
undefinedexists! If you want an array that can't be sparse, write one, but you're much better off learning to use iterators andforEachproperly.AlexGalays commentedon May 28, 2021
I see your point but almost nobody use the optional sparse capabilities of Arrays in their production applications. If types can help alleviate the historicaly poor technical choices of JS, why not do it.
thw0rted commentedon May 28, 2021
That's exactly what this issue was about! One could argue that sparse arrays are "one of the bad parts", sure. The fix for that, IMHO, is forcing you to check your indexed access, or avoid it altogether.
nh2 commentedon Oct 23, 2021
Oliver Joseph Ash (@OliverJAsh) Coul dyou edit the issue description to mention that
--noUncheckedIndexedAccessin TypeScript 4.1 fixes this?The thread is so long and still continuously edited that the solution is hard to find.
FE: Configure typescript to warn on undefined index access