如何在TypeScript中约束defaultItem仅为items数组内的id值?
TypeScript如何约束defaultItem为items数组中的id值之一?
需求场景
现有如下接口定义:
interface Item { id: string; name: string; } interface Foo { items: Item[]; defaultItem: Foo[number]['id'] }
希望在定义对象时,defaultItem只能取items数组中已存在的id值,比如下面的代码中defaultItem: 'fooooo'应该报错,仅允许为'bar'或'baz':
const myObj: Foo = { items: [{ id: 'bar', name: 'Bar' }, { id: 'baz', name: 'Baz'} ], defaultItem: 'fooooo' // 期望报错 }
已尝试的无效方案
- 泛型接口方案:将
Foo定义为泛型,但TypeScript最终会把defaultItem的类型推断为string,无法实现约束:
interface Foo<TItems extends Item[]> { items: TItems; defaultItem: TItems[number]['id'] }
- 给
items添加as const:直接添加后无效果,defaultItem仍未被正确约束。
可行解决方案
方案一:使用辅助函数推导泛型参数
通过辅助函数让TypeScript自动推导items的具体类型,从而约束defaultItem为对应id的联合类型:
interface Item { id: string; name: string; } interface Foo<TItems extends Item[]> { items: TItems; defaultItem: TItems[number]['id']; } // 辅助函数,自动推导泛型参数 function createFoo<T extends Item[]>(foo: Foo<T>) { return foo; } // 合法示例:defaultItem为items中存在的id const validObj = createFoo({ items: [{ id: 'bar', name: 'Bar' }, { id: 'baz', name: 'Baz' }], defaultItem: 'bar' }); // 非法示例:输入不存在的id会报错 const invalidObj = createFoo({ items: [{ id: 'bar', name: 'Bar' }, { id: 'baz', name: 'Baz' }], defaultItem: 'fooooo' // 报错:类型'"fooooo"'不能赋值给类型'"bar" | "baz"' });
原理:直接使用Foo<T>声明变量时,TypeScript会将TItems宽泛地推断为Item[],导致id类型为string;而通过辅助函数,TypeScript会根据传入的具体数组推导T的字面量类型,从而精准约束defaultItem的可选值。
方案二:结合as const与泛型类型提取
通过as const将数组转为只读元组,提取其类型后传给泛型接口,实现精准约束:
// 改用type定义,方便配合readonly修饰 type Item = { readonly id: string; readonly name: string; }; type Foo<T extends readonly Item[]> = { items: T; defaultItem: T[number]['id']; }; // 方式一:直接在对象中使用as const并指定泛型类型 const obj1: Foo<typeof obj1.items> = { items: [{ id: 'bar', name: 'Bar' }, { id: 'baz', name: 'Baz' }] as const, defaultItem: 'fooooo' // 报错:类型'"fooooo"'不能赋值给类型'"bar" | "baz"' }; // 方式二:先定义常量数组再使用 const items = [{ id: 'bar', name: 'Bar' }, { id: 'baz', name: 'Baz' }] as const; const obj2: Foo<typeof items> = { items, defaultItem: 'baz' // 合法 };
原理:as const会将数组元素的属性转为字面量类型,同时数组变为只读元组;通过typeof提取该元组类型并传给Foo泛型,defaultItem的类型会被解析为元组中所有id的联合类型。
内容的提问来源于stack exchange,提问作者aborted
相关产品推荐
相关产品推荐

