TypeScript类方法中联合类型与类型守卫的高效使用方案
TypeScript 联合类型返回值的类型守卫优化方案
现有实现的核心问题
当前用typeof result === 'object'做类型判断存在三个明显缺陷:
- 存在硬编码魔术字符串,代码可维护性差
- JS 中
typeof对所有非函数引用类型都返回object,只要方法成功返回值是对象而非原始类型,判断逻辑会直接失效 - 存在历史遗留bug:
typeof null === 'object',极端场景下会出现误判
推荐实现方案
方案1:可辨识联合(Discriminated Union,工业界最通用方案)
这是TypeScript官方推荐的联合类型收窄方案,核心思路是给联合类型里的不同分支添加唯一的、字面量类型的判别字段,TS可以根据这个字段自动完成类型收窄,完全不需要额外的复杂判断逻辑,且100%避免类型冲突。
首先改造你的错误类,添加唯一判别标签:
class OpError { // 用as const将字段值固定为字面量类型,供TS做类型收窄 readonly _type = 'op_error' as const; message: string; constructor(msg: string) { this.message = msg; } }
业务代码里的类型判断可以直接简化为:
const result = myBar.getFooName(myFoo); if (result._type === 'op_error') { // TS自动将result收窄为OpError类型,可直接访问message属性 console.log(result.message); } else { // TS自动将result收窄为成功返回值类型(此处为string) console.log(result); }
这个方案的优势:
- 不受返回值类型影响:哪怕成功返回值是对象、数组等引用类型,只要它不携带值为
'op_error'的_type字段,判断就不会出错 - 序列化场景兼容:如果返回值需要经过JSON序列化/反序列化(比如接口请求返回),判别字段会完整保留,不会像
instanceof那样因为执行上下文变化失效 - 易扩展:后续如果要新增其他错误类型(比如
ParamError、AuthError),只需要给每个错误类分配唯一的_type字段值即可,判断逻辑可以平滑扩展
方案2:封装类型谓词守卫
如果你不方便修改现有错误类的结构,可以封装独立的类型守卫函数,用TS的类型谓词(is关键字)封装判断逻辑,避免重复写散落在业务代码里的判断条件。
/** * 判断入参是否为OpError类型 */ function isOpError(value: string | OpError): value is OpError { // 可根据场景调整判断逻辑,比如判断实例、判断必要字段是否存在 return value instanceof OpError; }
业务代码中直接调用守卫函数即可:
const result = myBar.getFooName(myFoo); if (isOpError(result)) { console.log(result.message); } else { console.log(result); }
这个方案适合纯类实例场景,判断逻辑统一收敛在单个函数内,后续调整判断规则不需要修改多处业务代码。注意如果存在跨realm(比如多iframe、跨vm执行)、序列化反序列化场景,instanceof判断会失效,此时优先选择方案1。
避坑说明
- 不要依赖
typeof做引用类型的区分:除了函数之外的所有引用类型,typeof都会返回object,无法区分不同的对象类型 - 判别字段必须用字面量类型:如果把
_type声明为普通string类型,TS无法根据字段值做精确类型收窄,必须加as const或者直接声明字段类型为对应的字符串字面量 - 项目中如果大量使用「成功返回值+错误对象」的联合返回模式,可以抽离公共错误基类,统一规范判别字段,减少重复代码
内容的提问来源于stack exchange,提问作者GGizmos
相关产品推荐
相关产品推荐

