深究TS的联合类型

314 阅读3分钟

官方文档的解释是:联合类型表示一个值可以是几种类型之一 例如:

type name = string | number

let myName1: name = '张三' // 正确
let myName2: name = 0 // 正确

从结果上看,这样的解释是没问题的,但是个人认为换种解释可能会更好:
联合类型表示一个值至少包含几种类型之一,且不能拥有几种类型都未规定的属性。这种解释和官网文档的解释可能存在有本质区别。

请看以下示例:

interface baseA {
   name: string,
   age: number
}

interface baseB {
  addres: string
}

示例分析:

接口baseA表示一个对象,其只具有string类型的name属性和一个number类型的age属性。
接口baseB表示一个对象,其只具有string类型的addres属性

那么:

// 表达式A
const stu: baseA | baseB = {
    name: '张三',
    addres: '北京'
}

上面这段代码按官网文档的解释来分析,是无法通过的。比较baseA,少一个age属性;比较baseB,多一个name属性。但是,结果是可以通过的。

结果分析:

这和TS类型兼容性有关系。

类型兼容

阅读官网文档后确认,只有目标类型的成员会被检查是否兼容,此处的目标类型是baseAbaseB,这个比较过程是递归进行的。

在上面的关于联合类型示例代码中:
在检查比较baseB时,由于只会检查作为目标类型的baseB,所以stu自身额外的name属性不会被报错。但是需要注意的是name属性也是在baseA类型种有声明过,所以在声明对象字面量时才不会报错。

// 表达式A
const stu: baseA | baseB = {
    name: '张三',
    addres: '北京',
    kkkk: ''
}

stu中存在有kkk属性,而baseAbaseB都未规定过该属性,所以这个表达式不会被通过,会报错多余了一个kkk属性

再来看一个示例1:

  interface base{
    name: string
    age:number
  }

  function showAsdw(params:base) {
    console.log('测试运行', params)
  }
  
    showAsdw({
       name:'张三',
       age: 12,
       addres: ''
     })     //  ERROR  只能指定已知的属性,而 base中没有规定 addres
  

上面这个代码都不难理解,简单分析下,在调用showAsdw函数时,可以很明确的知道它需要的是一个base类型,那么在声明对象字面量时就会去对比base类型,发现不符合规定的属性或类型时,便会报错。
再看示例2

  interface base{
    name: string
    age:number
  }

  function showAsdw(params:base) {
    console.log('测试运行')
  }
  
    const stu11 = {
    name: '张三',
    age: 24,
    addres: '地址是'
  }

  showAsdw(stu11) // 输出 测试运行

从逻辑上来看,示例1和示例2并无区别,只是示例2在进行类型校验时和示例略有不同。
一丶首先,在声明对象字面量stu11时,没有指定类型(ts会进行类型推断),所以不会报错;
二丶然后再调用showAsdw函数时,因为实际stu11已经有了类型(推断出的类型),那么就会去和目标类型进行对比,也就是base,在这个过程中,并不会对stu11额外的属性报错,这便是类型兼容

总结

到这里再去结合类型兼容去看上面联合类型的示例就好理解了。
一丶在声明stu对象字面量时,就已知它最起码需要符合的类型,所以在此时声明没有规定的属性就会报错,例如kkkk
二丶声明完成后,将stubaseAbaseB进行对比,此时后者是目标类型,所以stu在分别进行单独比较时便不会因为额外的属性而报错