带有索引签名的 keyof 类型运算符

keyof type operator with indexed signature

我正在阅读打字稿 docs 并阅读了这段代码摘录:

type Mapish = { [k: string]: boolean };
type M = keyof Mapish;

他们给出了如下解释:

Note that in this example, M is string | number — this is because JavaScript object keys are always coerced to a string, so obj[0] is always the same as obj["0"].

所以我的问题是为什么只 number?为什么不 booleanobject

TypeScript 支持 stringnumber index signatures, and (starting with TypeScript 4.4) symbol and pattern template literal index signatures。 JavaScript 中的对象只有 stringsymbol 键,所以 stringsymbol 和模式模板文字(它们是 [=12 的子类型) =]) TypeScript 中的索引签名应该很有意义。但是 number 是怎么回事?


数字索引签名专门用于支持 arrays 和其他类似数组的对象。如果使用数字索引索引数组会产生 TypeScript 错误:

const arr = ["a", "b", "c"];
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i].toUpperCase());
  // -----------> ~ <-- error, you can't index with a number? 
}

要求 non-idiomatic JavaScript 如 arr[String(i)] 会很不方便。它甚至无济于事;编译器无法真正区分 String(1) 和任何其他字符串之间的区别,因此它不会理解 arr[String(1)] 应该是数组的元素,而不是像 [=24= 这样的方法].

因此,即使对象实际上并没有 number 值键,TypeScript 假装 它们这样做是为了更自然地支持数组,因为数组非常通常与惯用 JavaScript 代码中的数字索引一起使用。


另一方面,没有人使用其他对象或 boolean 键索引到 JavaScript 个对象:

// nobody does this
const foo = { true: 1, false: 0, "[object Object]": 2 };
console.log(foo[Math.random() < 0.5]) // what
console.log(foo[{ a: 123 }]); // what are you doing

嗯,也许不是 nobody,但是像 foo[false]foo[{}] 这样的代码更可能是编程错误的情况非常罕见预期的代码。所以 TypeScript 没有理由允许 booleanobject 索引签名。


Playground link to code