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

React TypeScript中如何建模含二选一字段的状态?

问题

我需要定义一种Item类型,它要么持有自身的数值,要么通过ID引用其他项,具体定义如下:

interface Item {
  name: string;
  value: { valueLiteral: number } | { id: string };
}

但在React组件中根据value的类型分支渲染时,TypeScript会报错:

Property 'id' does not exist on type '{ valueLiteral: number; } | { id: string; }'.
Property 'id' does not exist on type '{ valueLiteral: number; }'.ts(2339)

如果把value和id拆成可选字段,虽然能避免错误,但会丢失类型安全性——TypeScript无法阻止同时存在/缺失两个字段的无效情况,不符合我的建模需求。

解决方案

要在保留类型安全性的前提下解决这个问题,核心是给TypeScript提供类型守卫,让它能正确识别联合类型的具体分支。

方法1:直接使用in操作符做类型窄化

不需要额外定义函数,直接在条件判断中用in操作符,TypeScript会自动完成类型窄化:

interface Item {
  name: string;
  value: { valueLiteral: number } | { id: string };
}

const item1: Item = {
  name: "foo1",
  value: { valueLiteral: 1 }
};
const item2: Item = {
  name: "foo2",
  value: { id: "some-id" }
};

function ItemComponent({ item }: { item: Item }) {
  if ('id' in item.value) {
    return <div>{item.name} - ID = {item.value.id}</div>;
  }
  // 此时TypeScript自动推断剩余类型为{ valueLiteral: number }
  return <div>{item.name} - VALUE = {item.value.valueLiteral}</div>;
}

方法2:自定义类型守卫函数

如果需要在多个地方复用类型判断逻辑,可以定义一个类型守卫函数:

interface Item {
  name: string;
  value: { valueLiteral: number } | { id: string };
}

// 自定义类型守卫,判断value是否为带id的类型
function isIdValue(value: Item['value']): value is { id: string } {
  return 'id' in value;
}

function ItemComponent({ item }: { item: Item }) {
  if (isIdValue(item.value)) {
    return <div>{item.name} - ID = {item.value.id}</div>;
  }
  return <div>{item.name} - VALUE = {item.value.valueLiteral}</div>;
}

方法3:使用可辨识联合类型(推荐)

如果可以调整数据结构,推荐给联合类型的子类型添加一个共同的区分字段(如type),这是TypeScript官方推荐的可辨识联合类型写法,语义更清晰,类型推断更准确:

// 定义可辨识联合类型,通过type字段区分分支
type Item = 
  | { name: string; type: 'literal'; valueLiteral: number }
  | { name: string; type: 'reference'; id: string };

const item1: Item = {
  name: "foo1",
  type: 'literal',
  valueLiteral: 1
};
const item2: Item = {
  name: "foo2",
  type: 'reference',
  id: "some-id"
};

function ItemComponent({ item }: { item: Item }) {
  if (item.type === 'reference') {
    return <div>{item.name} - ID = {item.id}</div>;
  }
  return <div>{item.name} - VALUE = {item.valueLiteral}</div>;
}

这种写法从根源上确保了Item只能是两种互斥类型之一,完全符合你的数据建模需求,同时彻底避免了类型错误。


内容的提问来源于stack exchange,提问作者Evanss

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 09:57:11