• Interface(接口分享)第一节


    一、接口初探

    • 有时候我们传入的参数可能会包含很多的属性,但是编译器只会检查那些必须的属性是否存在,以及类型是否匹配,而咱们要讲的接口其实就是用来描述下面这个例子里的结构,对于接口传入的数据咱们只关心它的外形,只关心他传入的对象是够满足咱们接口的限定条件,满足咱们就认定他是正确的
    function Animal(config: {name: string, age: number}) {
      console.log("我叫:" + config.name + "今年" + config.age + "岁了")
    }
    console.log(Animal({ name: "yxl", age: 24 }))
    // 下面重构
    interface Config {
      name: string
      age: number
    }
    function Animal(config: Config) {
      console.log("我叫:" + config.name + "今年" + config.age + "岁了")
    }
    /* 
    * 这里把接口限制的类型付给了变量myAnimal,即使咱们传入了接口中没有限制的属性,
    * 这里编译之后也不会出现报错,是因为接口类型检查不会对变量赋值进行检查
    * 如果咱们这里把Animal({name: "yxl", age: 24, color: "黑色的头发"})这样传参的话,
    * 接口类型检查会报出当前color属性没有在Config接口中进行限制,所以是不被允许的
    */
    let myAnimal = { name: "yxl", age: 24, color: "黑色的头发" } 
    console.log(Animal(myAnimal))
    

    二、可选属性

    • 接口里的属性有时候不是必须的,有的咱们可以用到,有的 用不到,这个时候可选属性接口就是一个不错的选择,可选属性的好处就是咱们可以对可能存在的属性进行预先定义,其次是能够捕获不存在属性是的一个错误
    interface Config {
      name: string
      age?: number
     // [propName: string]: any 字符串索引签名
    }
    function Animal(config: Config) {
      console.log("我叫:" + config.name + "今年" + config.age + "岁了")
    }
    /*
    * 这里将Animal传参中的age咱们故意打错了个字母,这个时候你就会发现当前接口会对这个属性进行检查
    * 接口会对当前ages报错,不存在于当前接口规范中,这个时候为了避免这种报错可以采用类型断言,告诉这个接口,
    * 当前穿的参数就是你的规范,这个时候可以发现错误消失了,还有另一种方法是通过索引签名的方式进行屏蔽错误
    * [propName: string]: any 这个索引签名就是为了你能够预见某个对象可能觉有某些特殊的用途而准备的。
    */
    console.log(Animal({ name: "yxl", ages: 24 } as Config))
    

    三、只读属性

    • 一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly 来指定只读属性:
    interface Point {
      readonly x: number
      readonly y: number
    }
    let p:Point = { x: 12, y: 14 }
    p.x = 15 // 错误
    /*
    * TypeScript 具有 ReadonlyArray<T> 类型,它与 Array<T> 相似,只是把所有可变方法去掉了,
    * 因此可以确保数组创建后再也不能被修改:
    */
    let a: number[] = [1, 2, 3]
    let aa: ReadonlyArray<number> = a
    aa[0] = 14; // Index signature in type 'readonly number[]' only permits reading.
    aa.push(4) // Property 'push' does not exist on type 'readonly number[]'.
    aa.length = 10 // Cannot assign to 'length' because it is a read-only property.
    a = aa as number[] // 类型断言为数字组成的数组
    a[0] = 15
    

    四、函数类型

    • 接口能够描述JavaScript中对象拥有的各种各样的外形。除了描述带有属性的普通对象外,接口也可以描述函数类型。接下来我们一起看一下函数类型接口是怎么使用的
    interface SearchFun {
      (source: string, subString: string): Boolean
    }
    let mySearch: SearchFun // @1
    /*
    * 这里对声明了一个匿名函数,并且将这个匿名函数付给了变量mySearch,并且同时
    * 给这个匿名函数进行了传参,当然这个传参遵循了SearchFun接口规范,
    * 接口会自动推断参数的类型,因为咱们已经将mySearch函数付给了SearchFun类型变量
    * 注意: 函数类接口不同于属性类接口,要求属性的变量相同,这里变量可以不同,只需要保证变量的类型为string就行
    * function(a, b): Boolean
    * mySearch = function (a, b): Boolean { 这个方案也可,因为@1中已经将函数付给了接口类型变量
    * */
    mySearch = function (source: string, subString: string): Boolean {
      let result = source.search(subString);
      return result > -1
    }
    console.log(mySearch("http://b28.sy.souyoung.com/ad/cpcAuction", "28")) // true
    

    五、可索引类型

    • TypeScript 支持两种索引签名:字符串和数字。 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number 来索引时,JavaScript 会将它转换成string 然后再去索引对象。 也就是说用 100(一个 number)去索引等同于使用'100'(一个 string )去索引,因此两者需要保持一致。接下来看看如何使用:
    /*
    * 第一种情况数字类型索引NumberArr的时候返回值是string 这个时候调用a[0] 得到了Bob
    * [index: number]: string
    * 第二种情况是字符串索引NumberArr的时候返回值是string or number,这个时候
    * 返回值是string调用:a["name"] 得到了Bob 返回值是number调用:a["name"] 得到
    * 了数字:1
    * [index: string]: string
    * let a: NumberArr = {
    *   "name": "Bob"
    * }
    * * [index: string]: number
    * let a: NumberArr = {
    *   "age": 24
    * }
    * */
    interface NumberArr {
      [index: number]: string
    }
    let a: NumberArr = ["Bob", "Jerry"]  // 可
    // let a: NumberArr = [55, 66] 不可
    console.log(a[0]) // 可
    class Animal {
      name: string
    }
    class Dog extends Animal {
      breed: string
    }
    interface NotOkay {
      [x: string]: Animal //  Numeric index type 'Animal' is not assignable to string index type 'Dog'.
      [y: number]: Dog
    }
    /*
    * 上边的例子很好的诠释了两种索引类型可以同时使用,但是这两者又必须
    * 遵循之间的规则,那就是数字索引的返回值必须是字符串索引返回值类型的子类型
    * 上边Animal并不是Dog的子类型,刚好弄反了,所以咱们在运行编译的时候会
    * 报错。下面是正确的使用方式
    * interface NotOkay {
    *  [x: string]: Animal
    *  [y: number]: Dog
    * }
    * 下面的例子是对类进行了约束
    * */
    interface Animal {
      // 定义类的name属性值
      name: string;
      // 定义类中的方法
      eat(name: string, gender: string): void
    }
    class Dog implements Animal {
      name: string;
      constructor(name: string){
        this.name = name;
      };
      // 注意接口定义的方法有参数,当你不传也不会报错
      eat() {
        console.log("defind success")
      }
    }
    var dog = new Dog("pika")
    dog.eat()
    class Cat implements Animal {
      name: string;
      constructor(name: string){
        this.name = name;
      };
      eat(name: string, gender: string) {
        console.log(`${this.name} is ${gender} cat like eat ${name}`)
      }
    }
    var cat = new Cat("herry")
    cat.eat("fish", "male")
    /*
    * 接口描述了类的公共部分,而不是私有跟公共两部分,他不会帮你检查类是够具有某些私有成员。
    * 当一个类去实现一个接口的时候,他只会对其实例部分进行检查。constructor存在于类的静态部分,所以不再检查范围内。
    */
    

    六、接口继承

    • 和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。下面是实际应用的例子一起看一下:
    interface Animal {
      name: string
      say(): void
    }
    interface Person extends Animal {
      work(): void
      closer: string
    }
    class Progremmer implements Person {
      closer: string;
      name: string;
      say(): void {
        console.log(`${this.name}说我喜欢穿${this.closer}`)
      }
      work(): void {
        console.log(`${this.name}说工作使我快乐!!!`)
      }
      constructor(name: string, closer: string) {
        this.name = name;
        this.closer = closer;
      }
    }
    let g:Person = new Progremmer("小明", "花衣服")
    g.say();
    g.work();
    /*
    * 上边中的接口继承了接口,就意味着person这个接口有了Animal的属性以及方法
    * 这个时候咱们定义的程序员类去实现这个Person,实现这个person接口就意味着类必须实现
    * 这个接口中的方法跟属性,上述例子有体现
    */
    

    七、混合类型

    • 接口能够描述 JavaScript 里丰富的类型。 因为 JavaScript 其动态灵活的特点,有时你会希望一个对象可以同时具有上面提到的多种类型。
    interface Counter {
      (start: number): string
      name: string
      say(str: string): void
    }
    /*
    * 声明一个接口,如果只有(start: number): string一个成员,那么这个接口就是函数接口,
    * 同时还具有其他两个成员,可以用来描述对象的属性和方法,这样就构成了一个混合接口。
    */
    function myCounter(): Counter {
      function counter(start: number): string {
        return ""
      }
    /*
    * function counter实现了接口的描述,成为了一个函数,因为myCounter是Counter类型* 的,所以counter还具有name属性跟say方法
    */
      counter.name = "yxl"
      counter.say = function (str: string) {
         console.log(`我要学习${str}`)
    }
      let myCounters: Counter = counter
      return myCounters
    }
    let s = myCounter();
    s(10);
    s.name= "sss";
    s.say("TypeScript");
    /*
    * 官方文档这里使用了类型断言
    * function counter(): Counter {
    *   let myCounters = (function (start: number) { }) as Counter
    *   myCounters.name = "yxl"
    *   myCounters.say = function (str: string) {
    *     console.log(`我要学习${str}`)
    * }
    *   return myCounters
    * }
    */
    
  • 相关阅读:
    WPF/MVVM 快速开始指南(译)
    Rose2003的安装和破解
    自定义vs代码段
    silverlight中Grid.ColumnDefinitions属性设置错误
    vs绑定和取消绑定项目和解决方案
    firefox浏览器中silverlight无法输入问题
    silverlight中当前上下文中不存在名称“InitializeComponent“错误
    inotify使用代码
    build android toochain on mac (gcc 4.4.3 compile)
    istat 一个不错的widget
  • 原文地址:https://www.cnblogs.com/yxld/p/13984248.html
Copyright © 2020-2023  润新知