TypeScript 中奇怪的联合类型行为

Strange union type behavior in TypeScript

谁能解释 TypeScript (2.4.1) 中的以下行为?

场景:我有一个按钮,可以是 "red" 或 "red and round"(修改)。我想用以下语法来描述它:

button.mods = "red";
button.mods = ["red", "round"];
button.mods = { red: true, round: false };

为了描述所有这些,我使用了以下接口:

interface HasMods<T extends string>{ 
    mods: T | T[] | { [key in T]?: boolean } 
}

interface Button extends HasMods<"round" | "red"> { 
}

好的,现在我们可以做一些测试了:

let b: Button;
b.mods = "red"; //ok, correct 
b.mods = "green"; //error, correct

b.mods = ["red"]; //ok, correct
b.mods = ["green"]; //error, correct

b.mods = {red: true}; //ok, correct
b.mods = {red: true, green: true}; //error, correct

到目前为止一切都很完美。 但现在成谜了:

b.mods = {red: true, map: false}; //ok, why ???

为什么值 "map" 对我的类型为 { [key in T]?: boolean } 的对象有效,其中 T 是 "red" | "round"? "map" 既不是 "red" 也不是 "round"。

实际上,所有数组方法在这里都有效 - "every"、"copyWithin" 等...

如果您查看数组的 proto 定义,"map" 是属性之一。

所以当你比较时 "map in T" 它是真的,因为它是在一个数组结构中比较的,如果它是一个对象它将是假的

console.log("map" in {"element":4}) // false
console.log("map" in [4]) //true