You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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' // 期望报错
}

已尝试的无效方案

  1. 泛型接口方案:将Foo定义为泛型,但TypeScript最终会把defaultItem的类型推断为string,无法实现约束:
interface Foo<TItems extends Item[]> {
  items: TItems;
  defaultItem: TItems[number]['id']
}
  1. 给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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 11:12:53