如何提取带索引签名类型中的已定义键?
问题:如何创建类型转换器提取带索引签名对象类型的已定义键?
TL;DR
需要实现一个TypeScript类型转换器,接收类型A后返回类型B——B包含A的所有已定义键,且值仅接受string类型。但当A带有索引签名时,当前实现无法正确提取已定义的具体键。
普通对象类型下的可行实现
我原本写的类型转换器在普通对象场景可以正常工作:
type AToB< T, K extends keyof T = keyof T > = { [id in K]: string }; const a = { x : {} } const b : AToB<typeof a> = { x : "这里可以正常识别键x" }
此时b的类型推导正常,能正确识别键x
带索引签名的对象类型出现问题
但当对象类型带有索引签名时,keyof无法获取到已定义的具体键:
type AWithSig = { [id : string]: {}; } const aSig : AWithSig = { y: {}, z: {} } const bSig : AToB<typeof aSig> = { y : "这里无法识别键y", z : "键z也不会被提示 :(" }
此时bSig无法识别y和z这两个已定义的键,但如果手动给类型指定具体键的话就能正常工作。
解决方案
问题核心在于:当给变量显式指定带索引签名的类型(比如AWithSig)后,TypeScript会丢弃具体的键信息,只保留索引签名的类型约束。要保留对象的具体键,需要让TypeScript自动推导类型,同时确保它符合索引签名的约束。
方案1:用satisfies保留推导类型 + 约束
让变量自动推导具体键,同时用satisfies确保它符合索引签名的规则:
type AWithSig = { [id : string]: {}; } // 自动推导aSig的具体键,同时约束它符合AWithSig的规则 const aSig = { y: {}, z: {} } satisfies AWithSig; // 原有的AToB可以直接正常使用 const bSig : AToB<typeof aSig> = { y: "现在能识别键y了", z: "键z也会自动提示!" };
方案2:修改AToB适配带具体键的索引签名类型
如果需要更通用的类型转换器,可以调整AToB,确保它能提取带索引签名类型中的具体键(前提是类型本身保留了具体键信息):
type AToB<T> = { [K in keyof T]: string; } type AWithSig = { [id : string]: {}; } // 同样需要保留具体键的推导 const aSig = { y: {}, z: {} } satisfies AWithSig; const bSig : AToB<typeof aSig> = { y: "正常工作", z: "完美识别" };
关键原理
- 显式指定带索引签名的类型时,TypeScript会将变量类型“收窄”到索引签名,丢失具体键的信息;
satisfies关键字可以在保留自动推导的具体键的同时,确保变量符合目标类型的约束;keyof处理带索引签名的类型时,默认只会返回索引签名的键类型(比如string),只有当类型本身保留了具体键时,才会返回这些具体键。
内容的提问来源于stack exchange,提问作者Murilo Rocha
相关产品推荐
相关产品推荐

