类
为了支持面向对象编程, ES2015 中引入了 class
关键字,Typescript 给予了全面支持。
JS 有关类的用法,可以参考 MDN,本文不再赘述。
类的属性类型
类的属性类型通常定义在最外层:
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
定义时可以赋初始值,TypeScript 会推断属性的类型。
在构造函数定义类的属性也是可以的,有一种利用成员修饰符的简便写法:
class Person {
// 简写形式的成员修饰符必须写上
constructor(public name: string, public age: number) {}
}
两种写法是等价的。
类的实现
implements关键字
类可以通过 implements
关键字实现外部类型条件(interface
和 type
),这样类就必须实现接口中定义的属性和方法:
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
}
}
实现多个接口
类可以实现多个接口:
// ...
class Dog implements Animal, Pet {
// ...
}
此时类必须实现所有接口中定义的属性和方法。
类与接口的合并
类可以实现接口,也可以与(同名)接口合并:
interface Person {
address: string
}
class Person {
constructor(public name: string, private age: number) {}
}
const p = new Person('Tom', 18)
console.log(p.address) // 不报错(undefined)
成员修饰符
类的成员(属性、方法)可以使用修饰符来控制可访问性,共有三种修饰符:public
、protected
和 private
。
public
类成员的默认可见性为 public
。表示该成员在任何位置访问:
class Person {
public name: string
constructor(name: string) {
this.name = name
}
}
const p = new Person('Tom')
console.log(p.name) // Tom
由于 public
是默认的,除非为了醒目和可读性,一般可以省略。
protected
protected
修饰符表示该成员只能在类内部和子类中访问:
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
修饰符表示该成员只能在类内部访问:
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
关键字表示:
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 {} // ✅
(仅)在抽象类中,可以定义抽象成员,表示还未实现的成员,子类必须实现:
abstract class Animal {
abstract makeSound(): void
}
class Dog extends Animal {
makeSound() {
console.log('wang wang !')
}
}