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
相关产品推荐
相关产品推荐

