TypeScript中如何同时定义对象属性名与属性值的类型
写法失效原因
你写的[key in Car]: [key in Color]存在两个语法问题:
- 映射类型的
in遍历语法,只能用在类型定义的索引签名位置(也就是冒号左边定义属性名的位置),不能直接写在冒号右边的属性值位置。 - 冒号右边的
[key in Color]会被TS识别为元组类型定义,但元组内部不支持in遍历语法,且内外两层遍历都用了key作为变量名,存在命名冲突,属于非法类型语法,自然无法正常运行。
你本质的需求是约束属性值必须是Color联合类型的成员,根本不需要在值位置再做一次遍历,直接把值的类型指定为Color即可。
正确实现方案
根据你是否要求对象必须覆盖所有Car类型的成员,有两种常见实现:
- 要求对象必须包含所有汽车品牌,不能缺省
可以直接写映射类型,或者用TS内置的Record工具类型,两种写法完全等价:type Car = "toyota" | "mazda" | "suzuki" | "porsche"; type Color = "white" | "black" | "yellow" | "red"; // 手写映射类型 const cars: { [brand in Car]: Color; } = { toyota: "red", mazda: "black", suzuki: "yellow", porsche: "white", // 缺任何一个品牌TS都会报缺少必填属性的错误 }; // 用Record简写 const cars2: Record<Car, Color> = { toyota: "red", mazda: "black", suzuki: "yellow", porsche: "white", }; - 只要求已写的属性名属于
Car、属性值属于Color,不需要覆盖所有品牌
给索引签名加可选标记,或者用Partial包裹Record即可:// 手写可选映射类型 const cars: { [brand in Car]?: Color; } = { toyota: "red", porsche: "white", // 只写部分品牌不会报错 }; // Partial + Record简写 const cars2: Partial<Record<Car, Color>> = { toyota: "red", porsche: "white", };
如果使用TS 4.9及以上版本,更推荐用satisfies运算符做类型校验,这种写法不会丢失对象字面量的字面量类型推导,使用更灵活:
const cars = { toyota: "red", porsche: "white", } satisfies Partial<Record<Car, Color>>; // 此时cars.toyota会被推导为精确的字面量类型"red",而不是宽泛的Color联合 // 如果写错属性名或属性值,TS会直接在编写时抛出错误 const badCars = { toyota: "blue", // 报错:"blue"不属于Color类型 byd: "red", // 报错:"byd"不属于Car类型 } satisfies Partial<Record<Car, Color>>;
内容的提问来源于stack exchange,提问作者Nadav
相关产品推荐
相关产品推荐

