TypeScript中如何限制对象仅使用指定对象的键?
现有如下TypeScript代码定义了MY_OBJECT:
export type ObjectDetails = { a: string, b?: string }; export type MyObjectType = { [key: string]: ObjectDetails }; export const MY_OBJECT: MyObjectType = { BANANA: { a: "a" }, LEMON: { a: "A" } } export type MyObjectKey = keyof typeof MY_OBJECT;
需要定义类型MyType,要求对应的对象只能使用MY_OBJECT中的键(无需全部使用),值类型为string或number。当前定义的MyType如下:
type MyType = { [key: MyObjectKey]: string | number };
但创建包含非MY_OBJECT键的对象时未触发编译错误,例如:
const newObject: MyType = { BANANA: "banana", APPLE: "apple" // <-- 此处应触发编译错误,但实际没有 };
推测原因是MyObjectType的键类型设为string,导致MyObjectKey实际为string。请问无需提前定义MY_OBJECT所有键的类型,该如何修改代码实现需求?
问题核心在于你给MY_OBJECT显式指定了MyObjectType类型,这个类型的键是string,导致TypeScript无法从字面量推导MY_OBJECT的具体键集合。要让TypeScript保留MY_OBJECT的字面量键信息,有两种可行方案:
方案1:用as const推导字面量类型
去掉MY_OBJECT的显式类型标注,改用as const让TypeScript保留具体的键和值的字面量类型:
export type ObjectDetails = { a: string, b?: string }; // 去掉MyObjectType标注,用as const锁定字面量结构 export const MY_OBJECT = { BANANA: { a: "a" }, LEMON: { a: "A" } } as const; // 此时MyObjectKey会被推导为"BANANA" | "LEMON" export type MyObjectKey = keyof typeof MY_OBJECT; // 用Partial+Record定义可选的合法键类型 type MyType = Partial<Record<MyObjectKey, string | number>>;
这时再创建包含非法键的对象,就会触发编译错误:
const newObject: MyType = { BANANA: "banana", APPLE: "apple" // 报错:“APPLE”不在类型“Partial<Record<"BANANA" | "LEMON", string | number>>”中 };
方案2:用satisfies约束结构同时保留键信息
如果需要确保MY_OBJECT符合MyObjectType的结构要求,可以用TypeScript 4.9+支持的satisfies关键字,既约束对象结构,又保留字面量键的推导:
export type ObjectDetails = { a: string, b?: string }; export type MyObjectType = { [key: string]: ObjectDetails }; // satisfies确保MY_OBJECT符合MyObjectType,同时不丢失字面量键信息 export const MY_OBJECT = { BANANA: { a: "a" }, LEMON: { a: "A" } } satisfies MyObjectType; export type MyObjectKey = keyof typeof MY_OBJECT; type MyType = Partial<Record<MyObjectKey, string | number>>;
这个方案兼顾了结构约束和键的精确推导,同样能对非法键触发编译错误。
原代码无效的原因
原代码中MY_OBJECT被显式标注为MyObjectType,而MyObjectType的键是string,所以typeof MY_OBJECT的键类型被抹平为string,MyObjectKey也就变成了string。此时{ [key: string]: string | number }允许任意字符串键,自然不会对APPLE这类键报错。
内容的提问来源于stack exchange,提问作者syydi

