TypeScript

TypeScript的接口

2020-10-10  本文已影响0人  浅忆_0810

接口是在面向对象编程中一种规范的定义,它定义了行为和动作的规范,起一种限制的作用,只限制传入到接口的数据

TypeScript中的接口类似于JAVA,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等

1. 属性类型接口

属性类接口一般用作对于json对象的约束(下面的代码还没有使用接口)

// ts定义方法中传入参数就是一种接口
function print1(str: string): void {
  console.log(str); // 约束只能且必须传入一个字符串参数
}
print1("string");

// 对json对象进行约束
function print2(obj: { name: string; age: number }): void {
  console.log(obj); // 约束只能传有带有name和age属性的对象
}
print2({ name: "张三", age: 18 });

function print3(obj: { name: string; age: 18 }): void {
  console.log(obj); // 约束只能传有带有name和age属性的对象,并且age必须为18
}
print3({ name: "张三", age: 19 }); // 报错

对批量方法进行约束:使用接口

通过interface关键词对接口进行定义

interface FullName {
  firstName: string; // 注意这里要;
  secondName: string;
}

function printName(name: FullName): void {
  console.log(name.firstName, name.secondName);
}
let obj: object = {
  firstName: "张",
  secondName: "三"
}
printName(obj); // 传入对象必须有firstName 和 secondName

let obj2: object = {
  firstName: "李",
  secondName: "四",
  age: 18
}
function printInfo(info: FullName): void { // 使用接口可以对批量的函数进行约束
  console.log(info.firstName + info.secondName + info.age);
}

printInfo(obj2); // 原则上只能传入只含有firstName和secondName的对象,但是如果写在外面传入也不会报错
/*
  上面这种方法在传入参数的时候不会报错,但是在函数内部使用info.age的时候就会报错,因为接口内部没有设置age属性,如果不想报错,函数内部使用形参的属性必须是由接口定义的
*/
printInfo({
  firstName: "李",
  secondName: "四",
  age: 18
}); // 通过这种方式传入就会直接报错

可选属性接口:和函数的传参一样,可以在接口处用?代表可选接口属性

interface FullName {
  firstName: string;
  secondName?: string;
}
function printName(name: FullName): void {
  console.log(name.firstName, name.secondName); // 张 undefined
}

printName({
  firstName: "张"
})

案例:利用TS封装ajax请求

interface Config {
  type: string;
  url: string;
  data?: string;
  dataType?: string;
}

function ajax(config: Config) {
  let xhr: XMLHttpRequest = new XMLHttpRequest();
  xhr.open(config.type, config.url, true);
  xhr.send(config.data);

  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 20) {
      if (config.dataType.toLowerCase() === "json") {
        console.log(JSON.parse(xhr.responseText));
      } else {
        console.log(xhr.responseText);
      }
    }
  };
}

//由于接口的要求,必须传入type和url
ajax({
  type: "get",
  data: "name=张三",
  url: "http://www.baidu.com/",
  dataType: "json"
});

2. 函数类型接口

函数类型接口用于对方法传入的参数和返回值进行约束,可通过接口进行批量约束

// 加密的函数类型接口
interface encrypt {
  (key: string, value: string): string
}

let md5: encrypt = function (key: string, value: string): string {
  // 函数必须是两个参数,并且类型对应接口,同时返回值必须是接口的返回值string
  return key + "---" + value;
}

console.log(md5("name", "张三"))

函数类接口类类型接口类型区别在于函数类接口不用写函数名,只需要现在后面的参数返回值等,而类类型接口需要限制方法名和属性等


3. 可索引类型接口

可索引接口通常用作对数组和对象进行约束(但是这个接口不常用)

// 对数组使用
interface Arr {
  [index: number]: string; // 定义索引必须为number,值为string,否则会报错
}
let arr: Arr = ["123", "456"];
/*
  其实该接口的用法同数组指定类型的定义
  let arr:number[] = [1,2,3]
  let arr:Array<string> = ["123","456"]
*/

// 对对象使用,想要约束对象的属性值时可以使用
interface Obj {
  [index: string]: string; // 定义索引必须为string,值为string,否则会报错
}
let obj: Obj = { name: "张三", age: "20" }; // age不能是number

3.1 可索引接口也可以用来对一个属性接口进行只读属性以及额外对象的属性检验

// 该接口可以限制一个对象必须有color和width,还可以有其他的属性
interface Arr {
  readonly color: string; // 只读属性,只能进行1次赋值,后面不能修改
  width: number;
  [propName: string]: any;
}

4. 类类型接口

类类型接口主要用于对类的约束,和抽象类相似

interface Animal { // 其实这个说是属性类型也没错,因为eat也可以说是一个属性
  name: string;
  eat(str: string): void; // 这个接口也可以用作对生成的类的实例化对象的检验
}

class Dog implements Animal {
  // 类类型接口通过这种写法限制类
  constructor(public name: string) { } // 类中必须有name属性和eat方法
  eat(str: string) {
    console.log(this.name + "吃" + str);
  }
}
let dog: Dog = new Dog("狗");
dog.eat("狗粮");

class Cat implements Animal {
  private age: number = 2; // 也可以有其他的属性,这点和抽象类不同
  constructor(public name: string) { }
  eat() {
    /*
      如果接口要字符串类型的参数,这里可以不传参可以传字符串类型的参数,如果接口要求不传参,这里就不能传参,否则报错
    */
    console.log(this.name + "吃鱼");
    return 123; // 接口为void或者any或者number时可以返回number,否则会报错,其余类型对应
  }
  public showAge() { // 也可以有其他的方法
    console.log(this.age);
  }
}

let cat: Cat = new Cat("猫");
console.log(cat.eat()); // 123
cat.showAge(); // 2

5. 混合类型接口

混合类型接口是讲多种类型接口混合从而合成一个集成多种条件限制的接口

// 如将函数类型与属性类型混用,创建一个含有属性的函数
interface Counter {
  (start: number): number;
  interval: number;
  reset(): void;
}

function getCounter(): Counter {
  let counter: Counter = function (start: number): number {
    return start++;
  } as Counter; // 必须进行断言,将这个函数当做一个Couter类型,否则会报错
  counter.interval = 123;

  counter.reset = function () {
    this.interval = 0;
  };
  return counter;
}

let c = getCounter();
// 这个混合类型限制的变量本身是个函数,但是有reset方法和interval属性
c(10);
c.reset();
console.log(c.interval);
c.interval = 5;
console.log(c.interval);

6 . 接口扩展

接口扩展与类的继承类似,可以用子接口扩展父接口,从而拿到多个接口的限制条件

interface Animal {
  eat(): void;
}

interface Person extends Animal { // 继承父接口的限制条件
  name: string;
  work(): void;
}

class Student implements Person { // 接口会同时将前面两者的接口限制合并
  constructor(public name: string) { }
  eat() {
    console.log(this.name + "吃饭");
  }
  work() {
    console.log(this.name + "上学");
  }
}

let stu: Student = new Student("小明");
stu.eat();
stu.work();
// 接口和继承相结合
interface Animal {
  eat(): void;
}
interface Plant {
  wait(): void;
}
// 也可以继承多个接口,用逗号隔开
interface Person extends Animal, Plant {
  name: string;
  work(): void;
}

class YoungPerson {
  constructor(public name: string) { }
  drink() {
    console.log(this.name + "喝水");
  }
}
// 混合继承和接口限制的类
class Student extends YoungPerson implements Person {
  constructor(name: string) {
    super(name);
  }
  eat() {
    console.log(this.name + "吃饭");
  }
  work() {
    console.log(this.name + "上学");
  }
  wait() {
    console.log(this.name + "停下");
  }
}

let stu: Student = new Student("小明");
stu.eat();
stu.drink();
stu.work();
stu.wait();

7 . 继承类类型接口

7.1 TypeScript允许类也可以当作接口来使用,所以也可以被接口所继承

class Control {
  private state: any;
}

// 继承类的接口可以继承到一个类的私有和包含属性,接口会检验一个类是否继承有该父类的这两个属性
interface SelectableControl extends Control {
  select(): void;
}
// 一个继承Control类的Button类,虽然state是private类型不能再内部调用.但是确实继承了这个属性,不报错
class Button extends Control implements SelectableControl {
  select(): void { }
}
// 只继承了Control类,内部可以定义其他方法
class Radio extends Control {
  select(): void { }
}
// 这个类会报错,因为没有继承Control类,没有state属性
class Input implements SelectableControl {
  select(): void { }
}
// 即使写了private的state也会报错,因为state是在上一个类中是私有的,不能在外部访问,两个state是不同的
class Input2 implements SelectableControl {
  private state = 123;
  select(): void { }
}
/*
  如果上面的Control类型是public,那么在Input2中的state只要是设置为public类型就不会报错,设置为其他类型会和接口不符合,则会报错
*/
上一篇下一篇

猜你喜欢

热点阅读