为何TypeScript在推断与模板字面量类型后生成错误类型?
TypeScript类型组合不符合预期的原因分析
给定以下TypeScript代码:
type Excuse<Excuses extends Record<string, string>> = { new (excuses: Excuses): keyof Excuses extends `${infer Key}` ? `${Key}: ${Excuses[Key]}` : never; }; const helpingTheReindeer = { helping: 'the reindeer', differentKey: 'hello' } as const; declare const Excuse0: Excuse<typeof helpingTheReindeer>; const excuse0 = new Excuse0({ ...helpingTheReindeer, }); type t0_actual = typeof excuse0;
实际得到的t0_actual类型是"helping: the reindeer" | "helping: hello" | "differentKey: the reindeer" | "differentKey: hello",而非预期的"helping: the reindeer" | "differentKey: hello"。即使对象被声明为常量,TypeScript依然会将所有键与所有值进行组合,以下是对这一类型系统行为的解释:
核心原因:类型层面的关联丢失
TypeScript处理联合类型时,不会自动维持键与对应值的绑定关系。在你的类型定义中:
keyof Excuses extends `${infer Key}` ? `${Key}: ${Excuses[Key]}` : never
keyof Excuses是联合类型"helping" | "differentKey",条件类型会对每个联合成员单独处理;- 当处理每个
Key时,Excuses[Key]会被解析为所有键对应值的联合("the reindeer" | "hello"),而非当前Key对应的专属值; - 最终TypeScript会将每个键与所有值进行拼接,生成所有可能的组合,而非原对象的键值对映射。
修复方案:用映射类型绑定键值关系
要实现预期的一一对应键值对类型,需要通过映射类型遍历每个键并绑定对应的值,再将映射结果转为联合类型:
type Excuse<Excuses extends Record<string, string>> = { new (excuses: Excuses): { [K in keyof Excuses]: `${K}: ${Excuses[K]}` }[keyof Excuses]; };
这里:
{ [K in keyof Excuses]:${K}: ${Excuses[K]}}生成一个与原对象结构一致的类型,每个键对应正确的拼接字符串;- 通过
[keyof Excuses]索引访问后,得到预期的联合类型"helping: the reindeer" | "differentKey: hello"。
内容的提问来源于stack exchange,提问作者Ermolaev.ID
相关产品推荐
相关产品推荐

