类实现接口
类类型接口是类遵循的某种约束和规则。
类可以通过关键字 implements
实现接口。
interface IPerson {
name: string;
eat(): void
}
class Person implements IPerson {
// 如果构造函数参数中显式设置属性的访问控制符为public,则会默认初始化该属性
constructor(public name: string) {}
eat(): void {
console.log( `${this.name} 吃饭` )
}
}
const p1 = new Person('mike')
console.log(p1)
此外通过上文可知,一个类只能继承另一个类,但有时不同类之间却有一些共同的特性,这时可以把共有特性提取成接口,使用类去实现。
列举网上的一个案例:门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它:
interface Alarm {
alarm(): void
}
class Door { }
class SecurityDoor extends Door implements Alarm {
alarm() {
console.log('SecurityDoor alarm')
}
}
class Car implements Alarm {
alarm() {
console.log('Car alarm')
}
}
一个类可以同时实现多个接口:
interface IPerson {
name: string;
eat(): void;
}
interface IPersonX {
age: number;
skill(): string;
}
class PersonX implements IPerson, IPersonX {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eat(): void {
console.log( `${this.name} 吃饭` )
}
skill(): string {
return `${this.name} age is ${this.age} sleep`
}
}
const p2 = new PersonX('nike', 12)
类的静态部分和实例部分
关于类需要明确的一点是,类有两部分,静态部分和实例部分。
通过以上方式定义的接口只会检查类实例的属性,对于类静态属性的检查需要单独定义接口。
首先通过下面例子思考一个问题:
interface PersonInterface {
name: string
age: number
eat(): void
}
const person: PersonInterface = {
name: 'mike',
age: 12,
eat() {
console.log('food')
}
}
class Person implements PersonInterface {
name: string
age: number
constructor() {}
eat() {
console.log('food')
}
}
接口PersonInterface很明显是一个对象类型的接口,为什么类实现接口时,类中存在constructor方法确不报错?那么类实现的接口究竟是对谁的约束?
事实上类实现的接口只是对类实例出来的对象的约束,只会对其实例属性进行检查。而TS中constructor方法则属于类的静态方法,即属于类的静态部分,对静态属性的约束需要额外定义接口。
TS中类的静态部分指的是这个类本身;
实例部分指的是类实例化出来的对象;
所以对类约束的接口需要通过以下方式定义:
interface IDogInterface {
new (name: string, age: number)
getName(): void
}
const Dog: IDogInterface = class {
name: string
age: number
constructor(name, age) {
this.name = name
this.age = age
}
static getName() {
console.log('dog')
}
}
再看下面例子:
interface PersonInterface {
name: string
age: number
eat(): void
}
class Person implements PersonInterface {
name: string
age: number
constructor(name, age) {
this.name = name
this.age = age
}
eat() {
console.log('food')
}
}
const person = new Person(33, 'mike')
以上类Person实现接口PersonInterface,在类中我们定义了两个公有属性name、age,并对其类型进行了约束,注意:只是给类实例定义了两个属性name、age。
创建实例时,调用构造方法constructor,该函数接收的两个参数的类型是实际上是any类型,和公有属性name、age没有关系。创建实例的过程和js是一致的(首先创建一个对象,然后初始化该对象,再然后赋值给this,最后返回this)。
但是如果通过实例修改name的值,如果类型不一致则会报错。
person.name = 22 // Type '22' is not assignable to type 'string'
由此可以看出类实现的接口仅仅是对类实例的约束。
所以对一个类的实例部分和静态部分的约束需要定义两个接口,一个用来检查静态部分,一个用来检查实例部分。
// PersonConstructor 用来检查静态部分的
interface PersonConstructor {
new (name: string, age: number): PersonInterface // 检查 constructor 并且创建的类必须是通过PersonInterface接口实现的
typename: string // 检查静态属性 typename
logname(): void // 检查静态方法 logname
}
// PersonInterface 用来检查实例部分的
interface PersonInterface {
// new (name: string, age: number) // 静态方法的检查也不能写在这里 否则会报错
log(): void // 定义实例方法 log
}
// 创建一个类,需要对类的静态部分(类本身)和实例部分都进行约束
const PersonClass: PersonConstructor = class implements PersonInterface {
name: string
age: number
static typename = 'Person type' // 定义名为 typename 的静态属性
static logname() { // 定义名为 logname 的静态方法
console.log(this.typename)
}
constructor(name: string, age: number) { // constructor 也是静态方法
this.name = name
this.age = age
}
log() { // log 是实例方法
console.log(this.name, this.age)
}
}
new (name: string, age: number): PersonInterface
这里的意思是该接口对类的静态部分进行检查,并且我们创建的类必须是通过 PersonInterface
接口实现的。
如果仅仅是这样定义的 new (name: string, age: number)
,那么满足该接口结构的类所实现的接口不会有限制。是不是有点绕。看例子说话:
interface IPersonConstructor<T> {
new (name: string, age: number): T
getName(): void
}
interface IPersonInterfaceX {
eat(): void
}
interface IPersonInterfaceY {
skill(): void
}
const PersonX: IPersonConstructor<IPersonInterfaceX> = class implements IPersonInterfaceX {
static getName() {
console.log('jack')
}
name: string
age: number
constructor(name, age) {
this.name = name
this.age = age
}
eat() {
console.log('food')
}
}
const px = new PersonX('nike', 23)
px.eat()
const PersonY: IPersonConstructor<IPersonInterfaceY> = class implements IPersonInterfaceY {
static getName() {
console.log('jack')
}
name: string
age: number
constructor(name, age) {
this.name = name
this.age = age
}
skill() {
console.log('food')
}
}
const py = new PersonY('jack', 34)
py.skill()
以上得出:满足接口IPersonConstructor的类,它实现的类可以实现 IPersonInterfaceX 和 IPersonInterfaceY 接口。而new (name: string, age: number): PersonInterface
接口结构的类,它所实现的接口,必须满足PersonInterface接口。
记住:静态属性和方法的检查、实例属性和方法的检查是不同的 Interface
接口继承类
class Point {
x: number
y: number
}
interface Point3d extends Point {
z: number
}
let point3d: Point3d = {x: 1, y: 2, z: 3}
接口也可以继承多个类:
class PointX {
x: number
}
class PointY {
y: number
}
interface Point3d extends PointX, PointY {
z: number
}
let point3d: Point3d = {x: 1, y: 2, z: 3}
接口继承接口
接口可以继承一个或多个接口。查看接口继承以及混合类型TypeScript(二)接口
类继承
一个类可以继承另一个类,查看TypeScript(三)类