Skip to content

为了支持面向对象编程, ES2015 中引入了 class 关键字,Typescript 给予了全面支持。

JS 有关类的用法,可以参考 MDN,本文不再赘述。

类的属性类型

类的属性类型通常定义在最外层:

typescript
class Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

定义时可以赋初始值,TypeScript 会推断属性的类型。

在构造函数定义类的属性也是可以的,有一种利用成员修饰符的简便写法:

typescript
class Person {
  // 简写形式的成员修饰符必须写上
  constructor(public name: string, public age: number) {}
}

两种写法是等价的。

类的实现

implements关键字

类可以通过 implements 关键字实现外部类型条件(interfacetype),这样类就必须实现接口中定义的属性和方法:

typescript
interface Person {
  name: string
  age: number
}

class Student implements Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

实现多个接口

类可以实现多个接口:

typescript
// ...
class Dog implements Animal, Pet {
  // ...
}

此时类必须实现所有接口中定义的属性和方法。

类与接口的合并

类可以实现接口,也可以与(同名)接口合并:

typescript
interface Person {
  address: string
}

class Person {
  constructor(public name: string, private age: number) {}
}

const p = new Person('Tom', 18)
console.log(p.address) // 不报错(undefined)

成员修饰符

类的成员(属性、方法)可以使用修饰符来控制可访问性,共有三种修饰符:publicprotectedprivate

public

类成员的默认可见性为 public。表示该成员在任何位置访问:

typescript
class Person {
  public name: string
  constructor(name: string) {
    this.name = name
  }
}

const p = new Person('Tom')
console.log(p.name) // Tom

由于 public 是默认的,除非为了醒目和可读性,一般可以省略。

protected

protected 修饰符表示该成员只能在类内部和子类中访问:

typescript
class Person {
  protected name: string
  constructor(name: string) {
    this.name = name
  }
}

class Student extends Person {
  constructor(name: string) {
    super(name)
    console.log(this.name) // Tom
  }
}

const p = new Person('Tom')
console.log(p.name) // ❌ Property 'name' is protected and only accessible within class 'Person' and its subclasses.

private

private 修饰符表示该成员只能在类内部访问:

typescript
class Person {
  public name: string
  private age = 8
  constructor(name: string) {
    this.name = name
  }
}

const p = new Person('Tom')
console.log(p.name) // Tom
console.log(p.age) // ❌ Property 'age' is private and only accessible within class 'Person'.

实际上,利用private修饰的成员并不是严格意义上的私有,ES2022引入了真正的私有成员关键字 #,建议改用这种写法。

为什么说 private 不是真正的私有?

一方面,编译成 JavaScript 后,private 关键字就被剥离了,这时外部访问该成员就不会报错。另一方面,由于前一个原因,TypeScript 对于访问 private 成员没有严格禁止,使用方括号写法([])或者 in 运算符,实例对象就能访问该成员。这种访问方式也被称为 soft private,意为不稳定、不严格的私有。

好在ES2022引入了真正的私有成员关键字 #,实现了真正意义上的私有,即 hard private

抽象类

抽象类是不能被实例化的类,通常用于定义其他类的基类,用 abstract 关键字表示:

typescript
abstract class Animal {
  constructor(public species: string){}
}

// 抽象类不能被实例化
const a = new Animal('dog') // ❌ Cannot create an instance of an abstract class.

// 正确用法,当作基类
class Dog extends Animal { // ✅
  constructor() {
    super('dog')
  }
}

// 甚至抽象类的子类也可以是抽象类
abstract class Cat extends Animal {} // ✅

(仅)在抽象类中,可以定义抽象成员,表示还未实现的成员,子类必须实现:

typescript
abstract class Animal {
  abstract makeSound(): void
}

class Dog extends Animal {
  makeSound() {
    console.log('wang wang !')
  }
}