我不明白为什么在type T0
以下情况下是字符串而不是键为“ toString”,“ slice”“ split”的对象,以及为什么type T1
是对象键类型为string
的密钥,而现在我们的任何键都是string | symbol | number
?
type MappedType<T> = {
[K in keyof T]: T;
}
type T0 = MappedType<string>;
type T1 = MappedType<any>;
答案 0 :(得分:2)
可以在实现“同态pull request”的mapped types或T0
形式的映射类型中找到{[K in keyof T]: ...}
的权威答案,其中T
是通用类型参数。 (请注意,它最初被称为“同构”而不是“同构”,但是是同一回事。)
这种映射类型的正常使用情况是T
是某种objdct类型;在这种情况下,映射会保留T
中的键名以及它们的“只读”和“可选性”修饰符。 (可以通过在映射类型中显式设置它们来覆盖这些修饰符。)
那么当T
是像string
这样的原始类型时会发生什么?拉取请求显示“我们只是产生该原始类型”。事实证明,这非常有用,尤其是在您递归使用映射类型的情况下。像
type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> }
将遍历嵌套的对象类型,并使每个子属性为可选... 并保留基元:
declare const ab: DeepPartial<{ a: { b: string } }>;
const str = ab.a!.b!; // string
这几乎总是人们想要的。如果这种类型继续向下递归到诸如length
和toUpperCase
之类的已包装基元的所有属性中,则最终会产生大量无用的类型。然后,获得“正常”版本的唯一方法是使用conditional types终止下降而不是映射基元:
type DeepPartial2<T> =
{ [K in keyof T]?: T[K] extends object ? DeepPartial2<T[K]> : T[K] }
这不太方便(并且直到TS2.8引入条件类型才可能)。
从某种意义上讲,人们通常希望为同构映射的事物保留“相同的总体形状”,但是“相同的总体形状”的含义并不总是很明显。对象类型保持对象类型。原始保持原始。最初,数组被视为常规对象类型,产生了几乎无用的类型,将数组方法转换为无法识别的类型。这对任何人来说都不令人满意,这就是为什么TypeScript 3.1 changed it使得映射的数组类型仍然是数组类型,而映射的元组类型仍然是元组类型。
这使我们有了T1
,在any
的属性上进行同态映射会产生可索引的类型。
先前链接的拉取请求没有加入,但是实现者在comment到GitHub issue中解释了他的推理,并将此行为报告为错误:
keyof any
是string | number | symbol
,因此应用于any
的映射类型将产生{ [x: string]: XXX }
的等效值。
请注意,他说“等于”。 symbol
会自动从映射类型中删除,因此{[K in "a" | symbol]: string}
会产生{a: string}
。您可以 分别拥有string
和number
索引,因此{[K in string | number]: string}
是{[k: string]: string; [k: number]: string}
。但是由于数字键在TypeScript和JavaScript中被认为是字符串值,因此它与{[k: string]: string}
相同。 (如果您想详细了解string
,number
和symbol
键在映射时会发生什么,则可以看到this documentation。)
我个人并不是100%地将keyof any
映射到一个string
索引,而不是同时映射到string
和number
,因为{{1} }会有所不同...但这只是次要点。
重要的一点是评论的其余部分:
您可能会争辩说,在将同态映射类型应用于
{[K in keyof T]: K}
时,我们应该只产生any
,但这不太精确。例如,以any
实例化为{ [K in keyof T]: number }
的{{1}}实际上应该产生T
而不只是any
。
所以你去了; { [x: string]: number }
被视为具有可索引字符串的键的对象类型,因此在其上进行映射会产生合理的结果。该评论是我所看到的最接近该问题的规范答案。
好的,希望能有所帮助;祝你好运!