TypeScript:基于父对象键约束子对象属性值
TypeScript 类型约束:子对象属性与父对象键名一致的实现方案
需求概述
我需要在项目中强制执行这样的类型规则:子对象中的name字符串属性必须与其父对象对应的键名完全相同。简化示例如下:
const profile = { languages: { // 合法:键名与name一致 english: { name: "english" }, // 不合法:键名是"spanish"但name是"italian",二者必须匹配 spanish: { name: "italian" } } }
已知的泛型实现方式
我已经知道可以通过泛型来实现这个约束,但问题在于我的项目中Profile和Languages之间存在多层嵌套结构,传递泛型参数会非常繁琐:
export type Profile<TLanguageNames> = { languages: Languages<TLanguageNames>; }; export type Languages<TLanguageNames> = { readonly [TLanguageName in keyof TLanguageNames]?: { readonly name: TLanguageName; }; }; // 使用时需要手动指定泛型参数 const fields: Profile<{ english: ""; french: "" }> = { languages: { english: { name: "english" }, }, };
核心诉求
我希望不需要手动传递泛型参数,让TypeScript自动推断类型并实现上述约束。我尝试过直接定义非泛型类型,但这种写法无法达到预期效果(因为索引签名允许任意字符串键,无法绑定键名与name的值):
// 此代码无效,仅用于表达需求意图 export type Languages = { // 允许任意字符串作为键 readonly [TLanguageName in string]: { // 期望键名确定后,name必须与键名一致 readonly name: TLanguageName; }; }; // 这种写法下TypeScript不会报错,不符合需求 const fields: Languages = { english: { name: "french" }, };
可行的自动推断方案
直接定义非泛型类型无法实现这种“键值绑定”的约束,但可以通过泛型辅助函数让TypeScript自动推断类型,无需手动传递泛型参数:
// 定义一个辅助函数,通过泛型自动推断约束关系 const createProfile = <T extends Record<string, { name: keyof T }>>(profile: { languages: T }) => profile; // 合法示例:TypeScript自动验证所有键名与name匹配 const validProfile = createProfile({ languages: { english: { name: "english" }, french: { name: "french" } } }); // 不合法示例:TypeScript会直接报错,提示name与键名不匹配 const invalidProfile = createProfile({ languages: { spanish: { name: "italian" } // 错误:类型"italian"不能赋值给类型"spanish" } });
方案说明
- 泛型
T被约束为Record<string, { name: keyof T }>,意味着T中的每个键K,对应的子对象的name属性必须是K本身。 - 通过辅助函数创建对象时,TypeScript会自动推断
T的具体结构,无需手动指定泛型参数,完美解决多层结构下泛型传递的问题。 - 如果存在多层嵌套结构,只需要在最外层的辅助函数中定义泛型约束,内部层级会自动继承推断出的类型关系。
内容的提问来源于stack exchange,提问作者Miroslav Gregor
相关产品推荐
相关产品推荐

