TypeScript(四)类与接口

类实现接口

类类型接口是类遵循的某种约束和规则。

类可以通过关键字 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(三)类

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343