如何让TypeScript接口属性限制为数组对象的指定属性值集合?
问题:TypeScript 中基于数组元素属性限制接口属性类型
我有一个包含共同name属性的对象数组,示例如下:
const actionList = [{name: "a"}, {name: "b"}, {name: "c"}]
我希望定义一个接口,该接口接收此数组,同时另一个属性actionName只能是该数组中name属性的取值之一(即仅允许"a" | "b" | "c")。
我尝试了如下泛型接口:
interface Action{ name: string; } interface TestInterface<TActions extends Action[] = Action[]>{ actions: TActions; actionName: TActions[number]["name"] }
但仅使用TActions[number]["name"]得到的是泛化的string类型,无法实现预期的类型限制:
const a1:TestInterface = { actions: actionList, actionName: "" } // 应仅允许"a" | "b" | "c" const a2:TestInterface = { actions: actionList, actionName: "a" } // 应合法 const a3:TestInterface = { actions: actionList, actionName: "m" } // 应报错
请问是否可以实现这一需求?
解决方案
要实现这个需求,核心是让TypeScript推导出数组中name字段的具体字面量类型,而非宽泛的string。以下是具体实现步骤:
1. 保留数组的字面量类型
定义数组时使用as const,让TypeScript保留每个name的具体字符串字面量,同时将数组标记为只读:
const actionList = [{name: "a"}, {name: "b"}, {name: "c"}] as const;
2. 调整泛型接口约束
修改接口,将泛型参数约束为只读的Action类型数组,确保能兼容as const生成的数组类型:
interface Action { name: string; } interface TestInterface<TActions extends readonly Action[]> { actions: TActions; actionName: TActions[number]["name"]; }
3. 使用接口(自动推导或显式指定泛型)
显式指定泛型:直接传入数组的类型,此时
actionName会被严格限制为数组中name的取值集合:const a2: TestInterface<typeof actionList> = { actions: actionList, actionName: "a" }; // 合法 const a3: TestInterface<typeof actionList> = { actions: actionList, actionName: "m" }; // 报错:类型"m"不能赋值给"a" | "b" | "c"用函数自动推导(更便捷):通过函数参数自动推导泛型,无需手动指定:
function createTestConfig<TActions extends readonly Action[]>(config: TestInterface<TActions>) { return config; } const validConfig = createTestConfig({ actions: actionList, actionName: "b" }); // 合法 const invalidConfig = createTestConfig({ actions: actionList, actionName: "x" }); // 报错
原理说明
as const会将数组转换为只读的字面量类型数组,TypeScript不会将name的类型拓宽为string,而是保留具体的"a"、"b"、"c"。- 将泛型约束改为
readonly Action[],确保接口能接受as const生成的只读数组类型。 - 此时
TActions[number]["name"]会被推导为"a" | "b" | "c"联合类型,从而实现对actionName的严格类型限制。
内容的提问来源于stack exchange,提问作者Lwyrn
相关产品推荐
相关产品推荐

