jiajianrong
jiajianrong

Reputation: 928

why `typeof Map2<any>` returns a hint of 'number'?

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

Answers (1)

jcalz
jcalz

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 of string, 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

Related Questions