如何在为对象字面量加类型约束时仍推导其键类型?
解决方案
要同时实现对象结构约束和保留具体键的类型推断,可以用泛型工具函数或satisfies关键字来替代直接给对象加索引类型注解。直接使用{[key: string]: Fruit}会让TypeScript把对象类型拓宽为通用索引类型,丢失具体键的信息;而下面的方法能在约束结构的同时,保留原对象字面量的具体类型。
方法一:泛型工具函数(推荐,易复用)
定义一个泛型函数,约束传入的对象必须符合指定结构,同时返回原对象的具体类型:
type Fruit = { color: string; taste: string; }; // 泛型函数约束对象结构,同时保留原类型 const createFruitData = <T extends Record<string, Fruit>>(data: T) => Object.freeze(data); // 创建data时,既会检查每个值是否符合Fruit结构,又保留具体键的类型 const data = createFruitData({ apple: { color: "red", taste: "sweet" }, banana: { color: "yellow", taste: "sweet" }, peach: { color: "orange", taste: "sweet" }, grape: { color: "violet", taste: "sweet" }, }); type data_key = keyof typeof data; // 现在得到的是:type data_key = "apple" | "banana" | "peach" | "grape"
原理
T extends Record<string, Fruit>确保传入的对象所有值都符合Fruit类型,编辑时少写color或taste会直接报错,提供字段完整性提示。- 函数返回
T类型而非通用索引类型,所以typeof data会保留原对象的具体键和值类型,keyof typeof data自然能得到具体的键联合类型。
方法二:satisfies关键字(TypeScript 4.9+支持)
无需额外定义函数,用as const保留字面量类型,结合satisfies约束结构:
type Fruit = { color: string; taste: string; }; const data = Object.freeze({ apple: { color: "red", taste: "sweet" }, banana: { color: "yellow", taste: "sweet" }, peach: { color: "orange", taste: "sweet" }, grape: { color: "violet", taste: "sweet" }, }) as const satisfies Record<string, Fruit>; type data_key = keyof typeof data; // 同样得到:type data_key = "apple" | "banana" | "peach" | "grape"
原理
as const让对象的键和值都保留字面量类型,避免TypeScript自动拓宽类型。satisfies关键字用来验证对象结构是否符合Record<string, Fruit>,但不会改变对象本身的类型,因此keyof typeof data仍能获取具体的键联合类型。
内容的提问来源于stack exchange,提问作者rlight
相关产品推荐
相关产品推荐

