TypeScript如何实现联合类型收窄 正确访问不同类型独有属性
问题原因
该报错的本质是:当联合类型的多个分支没有公共可辨识字段时,TypeScript 静态检查阶段不会允许直接访问仅存在于单个分支上的属性,必须通过类型收窄让编译器确认参数的具体子类型,才能获得正确的类型推导。这个场景不需要使用泛型,通过TS内置的类型守卫能力即可实现。
实现方案
方案1:使用in操作符收窄(无需修改原有类型定义)
in是TS内置的类型守卫语法,通过判断属性是否存在于对象上,自动将联合类型收窄到匹配的分支,正好匹配你“存在options属性就是A类型,不存在就是B类型”的判断逻辑:
type Options = { items: Array<string>; }; type A = { options: Options; a: string; }; type B = { b: number; }; type AB = A | B; function test(p: AB) { // 判断options属性是否存在,TS自动收窄类型 if ('options' in p) { // 此分支内p被推导为A类型,可安全访问options、a属性 console.log(p.options.items, p.a); } else { // 此分支内p被推导为B类型,可安全访问b属性 console.log(p.b); } }
如果需要通过a、b属性判断,也可以直接写if ('a' in p)/if ('b' in p),效果一致。
方案2:定义可辨识联合类型(复杂场景推荐)
如果后续联合类型的分支会扩展,推荐给每个类型加一个固定的字面量标识字段,构成可辨识联合,这是TS中联合类型的最佳实践:
type Options = { items: Array<string>; }; // 给每个类型加唯一的type字面量字段作为标识 type A = { type: 'a'; options: Options; a: string; }; type B = { type: 'b'; b: number; }; type AB = A | B; function test(p: AB) { // 通过判断type字段值收窄类型 switch(p.type) { case 'a': // 此分支p类型为A console.log(p.options.items, p.a); break; case 'b': // 此分支p类型为B console.log(p.b); break; } }
方案3:自定义类型守卫(需要封装判断逻辑时使用)
如果判断A/B类型的逻辑比较复杂,可以封装自定义类型守卫函数,通过is语法告诉TS类型判断的结果:
// 返回值p is A 表示:函数返回true时,p的类型为A function isTypeA(p: AB): p is A { // 这里可以写自定义的判断逻辑,比如判断options是否是对象、items是否为数组等 return typeof (p as A).a === 'string' && !! (p as A).options; } function test(p: AB) { if (isTypeA(p)) { // 此分支p类型为A console.log(p.options.items, p.a); } else { // 此分支p类型为B console.log(p.b); } }
注意事项
不要直接写if(p.a)/if(p.b)做判断:在类型没有收窄的前提下,TS静态检查时会认为AB类型上不存在a/b属性,直接抛出编译错误,必须先通过上述三种方式完成类型收窄,才能访问对应类型的独有属性。
内容的提问来源于stack exchange,提问作者Pavel
相关产品推荐
相关产品推荐

