Skip to content

类型兼容性

假定有两个类型 A、B,如果能够将 B 赋值给 A,那么 B 和 A 类型兼容。

那么如何判读两个类型是否兼容呢?这里就需要引入鸭子辩型法的概念。

鸭子辩型法(子结构辩型法)

鸭子辩型法并不是判断一个东西是否真的鸭子,而是判断一个东西是否具有鸭子的特征。

即:目标类型需要具有某一些特征,赋值类型只需要满足这些特征即可。

鸭子辩型法主要应用在对象类型函数类型,而对于基本类型,则是采用完全匹配的方式,因为基本类型差异巨大。

对象类型

tsx
interface Duck {
  sound: '嘎嘎嘎'
  swim(): void
}

let person = {
  name: '伪装成鸭子的人',
  age: 18,
  // 类型断言,将字符串'嘎嘎嘎'断言为字面量类型'嘎嘎嘎'
  sound: '嘎嘎嘎' as '嘎嘎嘎',
  swim() { console.log(`${this.name}正在游泳,并发出了${this.sound}的声音`) }
}

let duck: Duck = person // 可以正常赋值

但有意思的是,如果直接使用对象字面量进行赋值,是会报错的。

tsx
interface Duck {
  sound: '嘎嘎嘎'
  swim(): void
}

// 无法正常赋值
let duck: Duck = {
  name: '伪装成鸭子的人',
  age: 18,
  // 类型断言,将字符串'嘎嘎嘎'断言为字面量类型'嘎嘎嘎'
  sound: '嘎嘎嘎' as '嘎嘎嘎',
  swim() { console.log(`${this.name}正在游泳,并发出了${this.sound}的声音`) }
}

typescript 有这样的规则,使用字面量直接赋值的时候,会采用严格的规则判断,这个字面量是只有这个变量使用的,既然如此就应该必须按照这个变量的类型去定义。而如果是使用其他变量来赋值的话,由于无法控制其他变量,因此会采用较为宽松的规则。

函数类型

函数类型的兼容判断,主要出现在回调函数中。

tsx
interface Condition {
  (n: number, i): void
}

function sum(list: number[], callback: Condition) {
  let s = 0;
  list.forEach((n, i) => {
    if (callback(n, i)) {
      s += n;
    }
  });
  return s;
}

// callback形参传递了两个参数,但实参中只取了一个参数,但这并不会报错
sum([1, 2, 3, 4, 5], n => n%2);

函数类型兼容有这样的规则:

  • 实参可以少,但不可以多
  • 函数要求有返回值,就必须有返回值