TypeScript 接口

类型注解中接口的使用

  • 可以把接口当作描述类型的工具使用,有点类似 type 关键字的感觉。
  • 接口支持和 type 的交叉类型。
// 定义
interface afObj { // 这样写描述了一个对象结构
  name: string;
  age: number;
}
interface AddFunction { // 这样写描述了一个函数类型
    (a: number, b: number): number; 
}
// 使用
const obj: afObj = {
  name: 'xx',
  age: 13,
};
const add: AddFunction = (x, y) => {
    return x + y;
};

接口的继承

interface Parent {
  prop1: string;
  prop2: number;
}

// 继承
interface Child extends Parent {
  prop3: string;
}

const myObj: Child = {
  prop1: '',
  prop2: 2,
  prop3: '',
};

接口声明合并

同样的接口声明不会覆盖,而是合并

interface Parent {
  prop1: string;
  prop2: number;
}

// 继承
interface Parent {
  prop3: string;
}

const myObj: Parent = {
  prop1: '',
  prop2: 2,
  prop3: '',
};

抽象类与接口

抽象类和接口相似,都是一种比较特殊的类,抽象类是一种特殊的类,而接口也是一种特殊的抽象类,他们通常配合面向对象的多态性一起使用,虽然声明和使用都比较容易,但他们的作用在理解上会困难一点。

抽象类

在 OOP 语言中一个类可以有一个或多个子类,而每个类都有至少一个公有方法作为外部代码访问他的接口。而抽象方法就是为了方便继承而引入的。

抽象方法就是没有方法体的方法,所谓没有方法体是指在方法声明时没有花括号及其中的内容,而是在声明方法时直接在方法名后加上分号结束。另外,声明抽象方法时,还要使用关键字 abstract 来修饰声明。

只要在声明类时,有一个方法是抽象方法,那么这个类就是抽象类,抽象类也要使用关键字 abstract 来修饰。在抽象类中,可以有不是抽象的成员方法和成员属性,但访问权限不能使用 private 关键字修饰为私有的。

abstract class Animal {
  abstract name: string; // 抽象属性
  abstract fun1(): string; // 抽象方法
  fun2(): void {
    // 普通方法。不能使用 private 关键字修饰为私有的。
    console.log('fun2');
  }
}

class Cat extends Animal {
  name: string = 'xjj';
  fun1(): string {
    return 'Good';
  }
}

抽象类有什么作用呢?使用抽象类就包含了继承关系,它是为它的子类定义公共接口,将它的操作交给子类去实现。就是将抽象类作为子类重载的模板使用,定义抽象类就相当于定义的一种规范,这种规范要求子类去遵守。当子内继承抽象类以后,就必须把抽象类中的抽象方法按照子类自己的需求去实现。子类必须把父内中的抽象方法全部实现,否则子类还存在抽象方法,所以还是抽象类,也不能实例化对象。

接口技术

因为 TypeScrtip 只支持单继承,也就是说每个类只能继承一个父类。当声明的新类继承抽象类实现模板以后,它就不能再有其他父类了。为了解决这个问题,TypeScrtip 引入了接口。接口是一种特殊的抽象类,而抽象类又是一种特殊的类,所以接口也是一种特殊的类。如果抽象类中的所有方法都是抽象方法,那么我们就可以换另外一种声明方式————使用「接口」技术。

接口中声明的方法必须都是抽象方法,而且接口中的所有成员都必须有 public 的访问权限。类的声明是使用「class」关键字标识的,而接口的声明则是使用「interface」关键字标识的。

接口中所有的方法都要求是抽象方法,所以就不需要在方法前使用 abstract 关键字标识了。而且在接口中也不需要显式地使用 public 访问权限进行修饰,因为默认权限就是 public 的,也只能是 public 的。另外,接口和抽象类一样也不能实例化对象,它是一种更严格的规范,也需要通过子类来实现。

如果需要使用接口中的成员,则需要通过子类去实现接口中的全部抽象方法,然后创建子类的对象去调用在子类中实现后的方法。但通过类去继承接口时需要使用 implements 关键字来实现,而并不是使用 extends 关键字完成。如果需要使用抽象类去实现接口中的部分方法,也需要使用 implements 关键字实现。

// 定义接口
interface Animal {
  name: string;
  fun1(): void;
  fun2(): void;
}
// 使用接口
class Cat implements Animal {
  // 实现接口中的成员属性
  name: string = 'mimi';
  // 实现接口中的方法
  fun1(): void {
    console.log(this.name);
  }
  // 抽象方法
  fun2(): string {
    return 'miaomiao';
  }
}
// 接口变抽象类
abstract class Mammal implements Animal {
  // 实现接口中的成员属性
  name: string = 'wangwang';
  // 实现接口中的方法
  fun1(): void {
    console.log(this.name);
  }
  // 抽象方法
  abstract fun2(): string;
}
// 使用抽象类
class Dog extends Mammal {
  fun2(): string {
    return 'Woof!';
  }
}
// 继承的同时实现接口
class Fish extends Mammal implements Animal {
  fun2(): string {
    return 'Bubble';
  }
}

接口的使用案例

interface PayInterface {
  handle(price: number): void;
}

class AliPay implements PayInterface {
  handle(price: number) {
    console.log(`支付宝付款${price}`);
  }
}

class WePay implements PayInterface {
  handle(price: number) {
    console.log(`微信付款${price}`);
  }
}

function pay(type: string, price: number): void {
  let payObj: PayInterface;
  switch (type) {
    case 'alipay':
      payObj = new AliPay();
      break;
    case 'wepay':
      payObj = new WePay();
      break;
    default:
      throw new Error('不支持的支付方式');
  }
  payObj.handle(price);
}