TS的接口和类型兼容性

398 阅读4分钟

接口和类型兼容性

接口: interface

扩展类型 - 接口

扩展类型: 类型别名、枚举、接口、类

TypeScript的接口:用于约束类、对象、函数的契约(标准)

类似于电脑的接口或后端接口,其实就是一种标准

契约(标准)的形式:

  • API文档(弱标准)
  • 代码约束(强标准)

和类型别名一样,不会出现在编译结果中,和类型别名区别主要在类这一块

  1. 接口约束对象
    interface User {
      name: string
      age: number
      sayHello: () => void
      //sayHello(): void
    }
    let u: User = {
      name: "saiwen",
      age: 22,
      sayHello() {
        console.log("sayHello");
      }
    };

这不就是跟类型别名一样吗?个人建议使用接口约束对象。

  1. 接口约束函数

一个简单例子,数组中奇数相加之和

    interface Condition {
      (n: number): boolean
    }
    function sum(numbers: number[], callback: Condition): number {
      let sum = 0;
      numbers.forEach((n) => {
        if (callback(n)) {
          sum += n;
        }
      });
      return sum;
    }
    const result = sum([1, 3, 4, 5, 7], (n) => n % 2 !== 0);
    console.log(result)

对函数的约束也可以使用类型别名

    type Condition = (n: number) => boolean

接口可以继承

可以通过接口之间的继承,实现多种接口的组合。

    interface A {
        T1: string
    }
    interface B {
        T2: number
    }
    interface C extends A, B {
        T3: boolean
    }
    let u: C = {
        T1: '1',
        T2: 1,
        T3: true
    }

使用类型别名也可以实现继承,需要使用&,叫做交叉类型

    type C {
        T3: boolean
    } & A & B

区别是子接口不能够覆盖父接口成员,会报错,而交叉类型会对子类型中同名变量的类型进行合并,如

    type C {
        T1: number
        T3: boolean
    } & A & B
    let u: C = {
        T1: '1',  //T1类型为string和number,报错,会提示两种类型所有方法
        T2: 1,
        T3: true
    }

readonly

只读修饰符,修饰的目标是只读,不在编译结果中

interface User {
    readonly id: string
}

只能够赋初始值,后面不能更改

let arr: readonly number[] = [1, 2, 3];
arr = [2, 3, 4]

此时readonly修饰的是类型,此时数组的push pop等方法无效,而arr是可以改变的

interface User {
    readonly arr: string []
}
let u:User = {
    arr: ['s', 't']
}
u.arr.push('t');
//u.arr = ['t'];//报错

这种写法中,readonly修饰的是数组,不是类型,数组的地址是不能改变的。修饰对象中的属性只能用readonly,其他情况下使用const就可以了。 可以修饰数组和修饰类型一起使用,做到数组完全不能变。

类型兼容性

TS如何来判定两个类型是否兼容,可以实现互相赋值。

B -> A,如果能完成赋值,则B和A类型兼容。

TS使用鸭子辨型法(子结构辨别法):目标类型需要某一些特征,赋值的类型只要能满足某些特征即可。就像鸭子有会嘎嘎叫和会游泳两个特征。

以下是各种类型的规则

  • 基本类型:完全匹配

  • 对象类型:鸭子辨型法

        interface Duck {
            sound: '嘎嘎'
            swin(): void
        }
        let person = {
            name: '扮演鸭子',
            sound: '嘎嘎嘎' as '嘎嘎嘎',  //类型断言
            swim(){
                console.log('游泳')
            }
        }
        let duck: Duck = person; //可以赋值
    

    对象进行赋值时,只要符合某些特征就可以进行赋值

    ** 特殊情况** 直接使用字面量类型会报错,这本身也符合对Duck的类型约束

        let duck: Duck = {
            name: '扮演鸭子',
            sound: '嘎嘎嘎' as '嘎嘎嘎',  //类型断言
            swim(){
                console.log('游泳')
            }
        }; 
    

    只要右边是对象变量形式,都使用鸭子辨型法。 TS认为直接对duck赋值时,认为开发者明确知道自己在做什么,使用更严格的判断,而使用对象进行赋值时,该对象可能来源于一个函数,类型约束变的宽松,比如一个后端接口 ,我们需要使用其中个别属性,后端不可能还要分成两个对象来传。

  • 函数类型:一切无比自然

    interface Condition {
      (n: number): boolean
    }
    function sum(numbers: number[], callback: Condition): number {
      let sum = 0;
      numbers.forEach((n, i) => {
        if (callback(n, i)) {
          sum += n;
        }
      });
      return sum;
    }
    const result = sum([1, 3, 4, 5, 7], (n) => n % 2 !== 0);
    console.log(result)

参数:传递给目标函数的参数可以少,但不可以多

返回结果:要求返回时必须返回。类型要正确,不要求返回时随便,即使返回了也不会用到

接口实现扑克牌增加大小王