Update Note: TypeScript 4.1 introduced the
noUncheckedIndexedAccess
compiler option, which partially addresses the issues discussed in this article. When enabled, Index Signature access automatically includes| undefined
type.
Index Signatures in TypeScript are used frequently. They represent data structures that can be accessed through bracket notation []
, such as the built-in TypeScript type Array<T>
:
1 | interface Array<T> { |
In JavaScript, developers often prefer using objects over Map instances to implement mapping data structures. While Map is safer in some aspects, it has serialization issues—JSON.stringify()
cannot directly serialize Map objects, which causes inconvenience in frontend-backend data exchange. TypeScript calls object mappings Record, with the type definition: type Record<K extends keyof any, T> = { [P in K]: T }
.
However, if you expect to achieve complete type safety with TypeScript, you need to use Index Signatures carefully. The main issue is that the value types obtained through Index Signatures are not accurate:
1 | const map: { [key: string]: number } = { |
Here, accessing a non-existent key returns undefined
, but TypeScript’s inferred type doesn’t include this possibility. In web applications, we frequently use arrays and maps for batch data processing or data structure mapping, and this hidden undefined
can lead to runtime errors.
As early as 2017, the community raised this issue on microsoft/TypeScript. Some proposed directly modifying the Index Signature value definition by adding | undefined
. However, this solution has problems: when defining a map, we only want the type to indicate that values might not exist when retrieving them. Adding | undefined
to the value type definition changes the semantics and doesn’t prevent adding undefined
values to the collection. More importantly, this would affect iterator return types—for example, Object.values()
would also include undefined
in its return type, which is clearly not intended.
This problem remains unresolved to this day, with the root cause lying in TypeScript’s continued need to maintain full compatibility with JavaScript code. JavaScript’s design limitations inevitably impact TypeScript’s ability to build a more secure type system. This manifests not only in Index Signatures, but represents a much broader architectural constraint.
As a static-typed superset, TypeScript’s syntax design cannot conflict with JavaScript, which means it cannot restrict certain JavaScript features, nor can it do anything additional at runtime. Completely solving the type safety issues of Index Signatures would require breaking through these fundamental limitations.
I often wonder—if freed from JavaScript’s constraints, TypeScript might have become a more elegant language, but it would also lose its greatest advantage: gradual adoption and a vast ecosystem. The current design is more of a pragmatic compromise under real-world considerations.
Of course, faced with this reality, different developers make different choices: some choose to compromise, some maintain their code perfectionism through various workarounds, and others silently wish in their hearts—may the world be free of JS.