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

TypeScript 4.8.3中如何实现至少包含指定键之一的类型校验?

解决TypeScript交叉联合类型的属性访问错误问题

你的问题出在TypeScript对交叉联合类型的类型窄化逻辑上:虽然Message类型确保了destinationUserID和destinationAddress二选一,但直接用m?.destinationUserID !== undefined的检查方式,TS无法将其关联到联合类型的具体分支,所以会报错属性不存在。下面是几种可行的解决方法:

方法一:用in操作符做类型窄化(最直接)

in操作符是TypeScript官方认可的类型守卫手段,能准确帮TS推断当前对象属于联合类型的哪个分支:

type Message = {
  subject: string
  body: string
} & (
  { destinationUserID: string } |
  { destinationAddress: string }
)

// 示例函数,实际根据你的业务实现
const getEmailByUserUUID = (id: string) => `${id}@example.com`

const processMessage = (m: Message) => {
  const emailAddress = 'destinationUserID' in m
    ? getEmailByUserUUID(m.destinationUserID)
    : m.destinationAddress

  if (emailAddress === undefined) throw new Error('Cannot find email')

  // 后续发送消息逻辑...
}

当用'destinationUserID' in m判断时,TS会自动将m的类型窄化为包含destinationUserID的分支,此时访问该属性不会报错;反之,else分支会被推断为包含destinationAddress的分支,直接访问也安全。

方法二:自定义类型守卫函数(适合复杂场景)

如果需要更复杂的判断逻辑,可以自定义类型守卫函数,明确告诉TS当前对象的类型:

type Message = {
  subject: string
  body: string
} & (
  { destinationUserID: string } |
  { destinationAddress: string }
)

const getEmailByUserUUID = (id: string) => `${id}@example.com`

// 自定义类型守卫,判断是否包含destinationUserID
function hasUserDestination(m: Message): m is Message & { destinationUserID: string } {
  return 'destinationUserID' in m
}

const processMessage = (m: Message) => {
  const emailAddress = hasUserDestination(m)
    ? getEmailByUserUUID(m.destinationUserID)
    : m.destinationAddress

  if (emailAddress === undefined) throw new Error('Cannot find email')

  // 后续逻辑...
}

方法三:重构为带判别式的联合类型(更易维护)

如果允许调整类型定义,把Message拆分为带判别式的联合类型,不仅能解决类型错误,还能让结构更清晰:

// 拆分出两个明确的子类型,添加type作为判别式
type UserMessage = {
  type: 'user',
  subject: string,
  body: string,
  destinationUserID: string
}

type AddressMessage = {
  type: 'address',
  subject: string,
  body: string,
  destinationAddress: string
}

type Message = UserMessage | AddressMessage

const getEmailByUserUUID = (id: string) => `${id}@example.com`

const processMessage = (m: Message) => {
  const emailAddress = m.type === 'user'
    ? getEmailByUserUUID(m.destinationUserID)
    : m.destinationAddress

  if (emailAddress === undefined) throw new Error('Cannot find email')

  // 后续逻辑...
}

这种方式通过type字段的字面量类型,让TS能完美识别每个分支的属性,完全避免类型推断问题,同时类型定义更直观,后续扩展也更方便。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 20:55:10