Reputation: 928
The code is simple. Is that a bug for TS or VScode?
interface Map2<T> {
[key: string]: T;
}
type XXX = keyof Map2<string>
//hint~ type XXX = string | number
interface Map2<T> {
[key: string]: T;
}
type XXX = keyof Map2<boolean>
//hint~ type XXX = string | number
Upvotes: 0
Views: 28
Reputation: 329553
It's not a bug. This behavior was introduced in TypeScript 2.9. From the documentation:
Given an object type
X
,keyof X
is resolved as follows:
- If
X
contains a string index signature,keyof X
is a union ofstring
,number
, and the literal types representing symbol-like properties
The reason why this was done is to deal with the fact that TypeScript has the concept of number
keys, even though JavaScript really doesn't. But TypeScript pretends that number
is a valid key type for an object.
All non-symbol keys in JavaScript are actually strings, even the indexing keys of arrays. (e.g., Object.keys(["a","b"])
returns ["0", "1"]
) When you index into an array with a number, as in arr[0]
, the index is coerced into a string first and is treated like arr["0"]
. Therefore an object with a string
index can still be treated as an array-like object with numeric keys, as they will be coerced to string
when you use them.
In a fully sound and ideal TypeScript type system, there'd be a type called NumericString
which is the subtype of string
corresponding to the output of String(n)
for all number
values of n
. And TypeScript would automatically coerce number
to NumericString
when you index into an object with it. In that hypothetical world, a string
index would just be a string
index, and you'd still be able to use it like an array, and there'd be no incentive for anyone to want to add number
to keyof {[k: string]: any}
.
In this world, though, where arrays are represented in TypeScript as having number
keys, there's an issue if you only treat string
keys as non-number
keys. You'd get keyof {[n: number]: any}
is never
. Instead of fixing it by removing number
indices and changing them to NumericString
, they addressed it by adding number
as a pseudo-subtype of string
that only appears when you look at object keys.
So, there you go. It's intentional. If you don't want to see number
in your string
, you can use the suggested workaround of replacing keyof X
with Extract<keyof X, string>
.
Okay, hope that helps; good luck!
Upvotes: 1