结构类型系统 - 9 (兼容性)

结构类型系统

1. 接口的兼容性

  • 如果传入的变量和声明的类型不匹配,TS就会进行兼容性检查
  • 原来是Duck-Check,就是说只要目标类型中声明的属性变量在原类型中都存在就是兼容的
  // TS 不关心类型,只比较属性,属性可以多,不能少
  interface Animal {
    name: string;
    age: number
  }
  interface Person {
    name: string;
    age: number;
    speak: (words: string) => void
  }
  function getName(animal: Animal): string {
    return animal.name;
  };
  let p: Person = {
    name: "ruhua",
    age: 18,
    speak: () => { }
  }
  console.log(getName(p));

上面这个例子里,p 的接口是 Person,作为参数传入 getName() 时的接口是 Animal,
这是可以的,因为接口 Person 的属性,包含了 Animal 的属性,反过来就报错了。
比如下面的例子,就会报错:

  let a: Animal = {
    name: "ruhua",
    age: 18,
  }
  function getAge(animal: Person): string {
    return animal.name;
  };
  console.log(getAge(a)); // 报错:类型 "Animal" 中缺少属性 "speak",但类型 "Person" 中需要该属性

2 基本类型的兼容性

  let num: string | number = 1;
  let str: string = "hello";
  num = str; // num 的类型包含 str 的类型,当然可以被赋值。
  let num2: {
    toString(): string
  }
  let str2: string = "ruhua";
  num2 = str2 // 字符串本质也是一个对象,其内置就有 toString()

3 类的兼容性

  class Animal {
    name: string = ""
  }
  class Bird extends Animal {
    swing: number = 0
  }
  let a: Animal;
  a = new Bird();
  a = {name: "ruhua"} // 不管这个对象的具体类型,只要属性有就可以

4 函数的兼容性

参数多给了不行,其它的少给了不行

  // 比较参数
  type sumFn = (a: number, b: number) => number
  let sum: sumFn;

  function f1(a: number, b: number): number {
    return a + b
  }
  sum = f1;

  function f2(a: number): number {
    return a
  }
  sum = f2;

  function f3(): number {
    return 1
  }
  sum = f3;
  // 上面三次赋址,都是可以的,sum 的参数必须包含 fn 的参数。

  // function f4(a: number, b: number, c: number): number{
  //   return 1
  // }
  // sum = f4; // 报错,这里是个特殊行为,只有参数是可以少不能多的
  // 比较返回值
  type GetPerson = () => { name: string, age: number };
  let getPerson: GetPerson;
  function g1() {
    return { name: "string", age: 1, gender: 0 }
  }
  getPerson = g1;

  // function g2() {
  //   return { name: "string" }
  // }
  // getPerson = g2; // 报错: 提示缺少 age 属性
  // 返回值可以多,不能少

5 函数参数的协变

  type logFunc = (a: number | string) => void;
  let log: logFunc;
  function log1(a: number | string | boolean) {
    console.log(a);
  }
  log = log1
  // function log2(a: number) {
  //   console.log(a);
  // }
  // log = log2 // 报错,参数的类型可以多不能少

6 泛型的兼容性

判断兼容性的时候,先判断具体的类型,再进行兼容性判断

  interface Empty<T> {

  }
  let x!: Empty<string>;
  let y!: Empty<number>;
  x = y // x 和 y 都是空对象,是可以进行赋址操作的

  // interface Empty2<T> {
  //   data: T
  // }
  // let x2!: Empty2<string>; // {data: string}
  // let y2!: Empty2<number>; // {data: number}
  // x2 = y2 // x2 和 y2 这两个对象有具体的属性,且属性的类型是不一样的,所以不能赋址

7 枚举的兼容性

  • 枚举类型与数字类型兼容,并且数字类型与枚举类型兼容
  • 不同枚举类型之间是不兼容的。
  // 数字可以赋给枚举
  enum Colors { Red, Yellow }
  let c: Colors;
  c = Colors.Red; // = 0
  c = 1; // = Colors.Yellow
  c = "1"; // 报错

  // 枚举值可赋给数字
  let n: number;
  n = 1
  n = Colors.Red
posted @ 2022-05-29 21:12  真的想不出来  阅读(121)  评论(0)    收藏  举报