美文网首页js css html
TypeScript接口的使用(七)

TypeScript接口的使用(七)

作者: 未路过 | 来源:发表于2022-09-22 00:39 被阅读0次

    1. 接口的声明

    在前面我们通过type可以用来声明一个对象类型:

    type Point = {
      x: number
      y: number
    }
    

    对象的另外一种声明方式就是通过接口来声明:

    interface Point = {
      x: number
      y: number
    }
    

    接口类型也可以定义可选类型和只读属性,只读属性指我们再初始化之后,这个值是不可以被修改的

    // 通过类型(type)别名来声明对象类型
    // type InfoType = {name: string, age: number}
    
    // 另外一种方式声明对象类型: 接口interface
    // 在其中可以定义可选类型
    // 也可以定义只读属性
    interface IInfoType {
      readonly name: string
      age: number
      friend?: {
        name: string
      }
    }
    
    const info: IInfoType = {
      name: "why",
      age: 18,
      friend: {
        name: "kobe"
      }
    }
    
    console.log(info.friend?.name)
    console.log(info.name)
    // info.name = "123"
    info.age = 20
    
    
    

    2. 索引类型

    // 通过interface来定义索引类型
    interface IndexLanguage {
      [index: number]: string
    }
    
    const frontLanguage: IndexLanguage = {
      0: "HTML",
      1: "CSS",
      2: "JavaScript",
      3: "Vue"
    }
    
    
    interface ILanguageYear {
      [name: string]: number
    }
    
    const languageYear: ILanguageYear = {
      "C": 1972,
      "Java": 1995,
      "JavaScript": 1996,
      "TypeScript": 2014
    }
    
    
    

    3. 函数类型

    前面我们都是通过interface来定义对象中普通的属性和方法的,实际上它也可以用来定义函数类型:
    用接口定义函数类型


    image.png
    // type CalcFn = (n1: number, n2: number) => number
    // 可调用的接口
    interface CalcFn {
      (n1: number, n2: number): number
    }
    
    function calc(num1: number, num2: number, calcFn: CalcFn) {
      return calcFn(num1, num2)
    }
    
    const add: CalcFn = (num1, num2) => {
      return num1 + num2
    }
    
    calc(20, 30, add)
    
    
    

    4. 接口继承

    接口和类一样是可以进行继承的,也是使用extends关键字:
    并且我们会发现,接口是支持多继承的(类不支持多继承)

    interface ISwim {
      swimming: () => void;
    }
    
    interface IFly {
      flying: () => void;
    }
    
    interface IAction extends ISwim, IFly {}
    //支持多继承
    
    const action: IAction = {
      swimming() {},
      flying() {},
      //里面必须有这两个方法
    };
    
    
    image.png

    5.交叉类型

    image.png

    6.交叉类型的应用

    image.png
    // 一种组合类型的方式: 联合类型
    type WhyType = number | string;
    type Direction = "left" | "right" | "center";
    /* ,联合类型的话是把我们多个这个值,
    多个我们的类型,多个类型跟他联合在一起,到时候的话,你取里面的任何,一个类型都可以 */
    
    // 另一种组件类型的方式: 交叉类型
    type WType = number & string;
    
    interface ISwim {
      swimming: () => void;
    }
    
    interface IFly {
      flying: () => void;
    }
    
    type MyType1 = ISwim | IFly;
    type MyType2 = ISwim & IFly;
    
    const obj1: MyType1 = {
      flying() {},
    };
    
    const obj2: MyType2 = {
      swimming() {},
      flying() {},
    };
    
    export {};
    
    

    两个接口他们的组合的时候就有两种方式了,第一种方式的话是定义一个新的接口,然后让他们继承自我原来的这两个接口,那么这个接口里面就相当于把前面这两个就给他组合在一起了,另外一种方式,交叉类型,他也是可以把我们两个对象类型,两个对象类型跟他结合在一起,结合在一起之后的话,生成一个新的类型.

    7.接口的实现

    1.接口作为标识符的使用

    interface Iswim {
      swimming: () => void;
    }
    
    interface IEat {
      eating: () => void;
    }
    
    //这两个接口可以作为类型,比如在定义变量的时候
    const a: Iswim = {
      swimming() {},
    }; //接口作为标识符类型
    
    function foo(swim: Iswim) {
      //传入的这个对象必须满足这个接口
    }
    foo({ swimming() {} });
    
    1. 类可以作为接口
    interface Iswim {
      swimming: () => void;
    }
    
    interface IEat {
      eating: () => void;
    }
    // 类实现接口
    class Animal {}
    
    //类Fish除了可以继承Animal,也可以实现接口
    //但是继承的化只能实现单继承
    class Fish extends Animal implements ISwim, IEat {
      swimming() {
        console.log("Fish Swmming");
      }
    
      eating() {
        console.log("Fish Eating");
      }
      //因为实现了两个接口所以里面必须有swiming和eating
      /* 
      使用类实现接口的化,编写一一些公共的公共的API的话,在很多的情况下都可以调用的,对于可复用的一些API的时候,它就变得更加的灵活。那么我们就可以面向接口进行编程。
      */
    }
    
    //// 编写一些公共的API: 面向接口编程
    //function swimAction(swimable: Fish {
    //  swimable.swimming();
    //}
    swimAction(new Fish());
    //这么写的话,这个函数就不具备通用性,因为这个参数我们就只能传Fish类型的一个对象。
    //如果我们想swimAction({swimming: function() {}})是不可以的。只能new一个Fish对象
    
    //这个时候我们就可以面向接口编程
    function swimAction(swimable: ISwim) {
      swimable.swimming()
    }
    // 1.所有实现了接口的类对应的对象, 都是可以传入
    swimAction(new Fish())
    //如果Person也实现了 ISwim
    class Person implements ISwim {
      swimming() {
        console.log("Person Swimming")
      }
    }
    swimAction(new Person())
    swimAction({swimming: function() {}})
    /* 就意味着只要是有一个类,它实现了这个接口,到时候的话,它都是可以调动我公共的这个API的,那么我在编辑这个公共API的时候,我这里最好就是面向接口编程的 
    面向接口编程的话,就让我编写的这个API他更加具备通用性啦,在很多很多地方都可以被别人调用,只要你实现了我的这个接口,甚至是我们普通的对象字面量,你只要也是符合我接口对应的类型的,你也是可以传到我们这个里面去的,可以转到这个里面去,所以这个的话就叫做面向接口编程
    */
    
    export {};
    

    8.interface和type区别

    image.png
    interface IFoo {
      name: string;
    }
    interface IFoo {
      age: number;
    }
    /* interface允许定义两个名字相同的接口,意味着会把两个接口的内容合并在一起,相当于
    interface Ifoo {
      ame: string
      age: number
    }
     */
    const foo: IFoo = {
      name: "why",
      age: 18,
    };
    
    export {};
    
    // document.getElementById("app") as HTMLDivElement
    /* 
    刚才我们看到的Windows类型,包括document这些类型它来自哪里呢,这些类型来自于我们在安装typescript的时候,它会默认帮助我们安装一些lib的库,
    那安装的这些库里面,它就默认有帮助我们定义很多的类型,
    包括比如说这个javascript里面内置的一些类型项,Math,Data这些类型,包括这些内置类型,
    也包括dom的一些类型的API的话,
    Winodw类型,HTMLDivElement类型等
    06:01
    */
    //window这个类型没有age这个属性,我们如果想有age这个属性的化
    interface Window {
      age: number;
    }
    //这时候不能覆盖以前的Window,而是合并
    window.age = 19;
    console.log(window.age);
    
    //type根本不允许定义两个相同的类型
    // type IBar = {
    //   name: string
    //   age: number
    // }
    
    // type IBar = {
    // }
    
    
    
    

    9.字面量赋值

    interface IPerson {
      name: string;
      age: number;
      height: number;
    }
    
    const info: IPerson = {
      name: "why",
      age: 18,
      height: 1.88,
      //address: "广州市", //不能添加这个属性,因为IPerson没有定义
    };
    
    
    

    怎么让他不报错呢

    interface IPerson {
      name: string;
      age: number;
      height: number;
    }
    
    const info = {
      name: "why",
      age: 18,
      height: 1.88,
      address: "广州市",
    }; //info会做类型推导,推导出来
    /*  
    const info: {
        name: string;
        age: number;
        height: number;
        address: string;
    }
    
    */
    
    const p: IPerson = info;
    /* 
    这个时候把info类型和IPerson类型是冲突的,因为多了个address
    ts主要做类型检测,类型检测通过,你这个代码就没有问题
    fressness擦除
    当把info这个引用类型赋值给p的时候,他会把我们对应某一个属性给它擦除掉,擦出掉之后,也就是address,如果依然是满足你这里的这个类型的情况下,也就是 IPerson ,因为我们这三个是不是依然是满足的,依然是满足的情况下,就是可以赋值的。如果info里面没有age,你再把这个address擦掉之后,你是少一个的,少一个是不行的,同学们就是擦除掉那些多余的,擦掉那些多余的之后,
    我依然是符合你这类型的,他是允许我们这么赋值的,它允许这么赋值
    */
    
    console.log(info);
    console.log(p);
    /* { name: 'why', age: 18, height: 1.88, address: '广州市' }
    { name: 'why', age: 18, height: 1.88, address: '广州市' } */
    export {};
    
    
    

    这是因为TypeScript在字面量直接赋值的过程中,为了进行类型推导会进行严格的类型限制。
    但是之后如果我们是将一个 变量标识符 赋值给其他的变量时,会进行freshness擦除操作。

    应用

    interface IPerson {
      name: string;
      age: number;
      height: number;
    }
    
    function printInfo(person: IPerson) {
      console.log(person);
      // console.log(person.address);不可以使用address,因为对于类型检测来说,,人家认为你这个person里面只有三个属性,,在这里取address的时候,在类型检测这一步他是过不去的,但是可以直接打印所有
    }
    
    // 代码会报错
    // printInfo({
    //   name: "why",
    //   age: 18,
    //   height: 1.88,
    //   address: "广州市"
    // })
    
    const info = {
      name: "why",
      age: 18,
      height: 1.88,
      address: "广州市",
    };
    
    printInfo(info);
    //{ name: 'why', age: 18, height: 1.88, address: '广州市' }
    export {};
    
    

    10.TypeScript枚举类型

    image.png
    //,其实枚举类型就是定义一组常量,并且给他放到我们对应的一种数据类型里面
    // type Direction = "left" | "Right" | "Top" | "Bottom"
    
    enum Direction {
      LEFT,
      RIGHT,
      TOP,
      BOTTOM,
    } //代码阅读性强
    /* 
    看起来这些东西是我们的一些标识的一些常量,但是其实本质上他们是一些数字常量,也就第一个的话其实是一个零,然后第二个的话是一个一,第三个的话是一个二,然后第四个的话就是一个三,它内部是由我们这里这么一些数字的值的,只不过这些值你不需要去写,你不写的时候,他们默认也是有的,但是能改变这里这些东西的值
    enum Direction {
      LEFT = 0,
      RIGH = 1,
      TOP = 2,
      BOTTOM = 3,
    } 默认
    
    enum Direction {
      LEFT = 100,
      RIGH = 200,
      TOP = 300,
      BOTTOM = 400,
    }
    enum Direction {
      LEFT = 100,
      RIGH ,//101
      TOP ,//102
      BOTTOM,//103
    }
    */
    function turnDirection(direction: Direction) {
      console.log(direction);
      switch (direction) {
        case Direction.LEFT:
          console.log("改变角色的方向向左");
          break;
        case Direction.RIGHT:
          console.log("改变角色的方向向右");
          break;
        case Direction.TOP:
          console.log("改变角色的方向向上");
          break;
        case Direction.BOTTOM:
          console.log("改变角色的方向向下");
          break; //如果少些一个case的化,代码会报错。因为default用了never类型
        default:
          const foo: never = direction;
          break;
      }
    }
    
    turnDirection(Direction.LEFT);
    turnDirection(Direction.RIGHT);
    turnDirection(Direction.TOP);
    turnDirection(Direction.BOTTOM);
    
    export {};
    /* 
    0
    改变角色的方向向左
    1
    改变角色的方向向右
    2
    改变角色的方向向上
    3
    改变角色的方向向下
    */
    

    11.枚举类型的值

    image.png

    相关文章

      网友评论

        本文标题:TypeScript接口的使用(七)

        本文链接:https://www.haomeiwen.com/subject/ithbortx.html