Published on

TypeScript - Classes

Authors
  • avatar
    Name
    Deng Hua
    Twitter

目录

Annotating Classes In TypeScript

在typescript中,我们不能直接在构造函数中初始化一个属性而不“事先通知”TS

class Player {
  constructor(first: string, last: string) {
    this.first = first //  ❌ 类型“Player”上不存在属性“first”
    this.last = last   //  ❌ 类型“Player”上不存在属性“last”
  }
}

属性注释

class Player {
  first: string
  last: string

  constructor(first: string, last: string) {
    this.first = first
    this.last = last
  }
}

const elton = new Player('Elton', 'Steele')

Readonly Class Properties

在类中使用readonly语法

class Player {
  readonly first: string
  readonly last: string

  constructor(first: string, last: string) {
    this.first = first
    this.last = last
  }
}

const elton = new Player('Elton', 'Steele')

elton.first = 'elton' // ❌ 无法为“first”赋值,因为它是只读属性。

The Public Modifier

默认情况下,在javascript或typescript中,类中的每个属性,每个方法都被认为是公共的,它们都能被所属的实例访问。

当然也可以显式的使用public关键字注释属性。注意这是typescript的语法。

class Player {
  public first: string
  public last: string

  constructor(first: string, last: string) {
    this.first = first
    this.last = last
  }
}

const elton = new Player('Elton', 'Steele')

elton.first // ✅

这样明确了该属性是否是可写的,可访问的等等。

当然,也可以与readonly搭配使用,即只可访问。

class Player {
  public readonly first: string
  public last: string

  constructor(first: string, last: string) {
    this.first = first
    this.last = last
  }
}

const elton = new Player('Elton', 'Steele')

elton.first = 'john' // ❌ 无法为“first”赋值,因为它是只读属性

The Private Modifier

使用private关键字,可以使属性只能在类内部访问与使用,无法在类外部访问它。

class Player {
  private first: string
  public last: string

  constructor(first: string, last: string) {
    this.first = first
    this.last = last
  }
}

const elton = new Player('Elton', 'Steele')

elton.first // ❌ 属性“first”为私有属性,只能在类“Player”中访问

当然,我们知道在ES6中也有属性私有的标识符

class Player {
  private first: string
  #last: string

  constructor(first: string, last: string) {
    this.first = first
    this.#last = last
  }
}

const elton = new Player('Elton', 'Steele')

elton.first // ❌ 属性“first”为私有属性,只能在类“Player”中访问
elton.#last // ❌ 属性 "#last" 在类 "Player" 外部不可访问,因为它具有专用标识符

但两者不能一同使用。

class Player {
  private first: string
  #last: string
  private #fullName: string // ❌  可访问性修饰符不能与专用标识符一起使用

  constructor(first: string, last: string) {
    this.first = first
    this.#last = last
  }
}

更推荐在TS中只使用private关键字。


补充类的私有方法:

class Player {
  private first: string
  last: string

  constructor(first: string, last: string) {
    this.first = first
    this.last = last
    this.privateMethod() // ✅ because this call in inside the class
  }

  private privateMethod() {
    console.log('secret method')
  }
}

const elton = new Player('Elton', 'Steele')

elton.privateMethod // ❌ 属性“privateMethod”为私有属性,只能在类“Player”中访问

Parameter Properties Shorthand

有一种更快捷的方式声明一个类

class Player {
  constructor(
    public first: string,
    public last: string
  ) {}

  private privateMethod() {
    console.log('secret method')
  }
}

const elton = new Player('Elton', 'Steele')

Getter and Setter

class Player {
  first: string
  last: string
  private _score: number

  constructor(first: string, last: string) {
    this.first = first
    this.last = last
  }

  get fullName(): string {
    return `${this.first} ${this.last}`
  }

  get score(): number {
    return this._score
  }

  set score(newScore: number) {
    this._score = newScore
  }
}

const elton = new Player('Elton', 'Steele')

elton.fullName
elton.score

elton.score = 99

The Protected Modifier

假设我们有一个子类需要继承之前的Player,但是我们想在子类中访问Player的私有属性。

class Player {
  first: string
  last: string
  private _score: number

  constructor(first: string, last: string) {
    this.first = first
    this.last = last
  }

  get fullName(): string {
    return `${this.first} ${this.last}`
  }

  get score(): number {
    return this._score
  }

  set score(newScore: number) {
    this._score = newScore
  }
}

class SuperPlayer extends Player {
  public isAdmin: boolean = true

  maxScore() {
    this._score = 999999 // ❌ 属性“_score”为私有属性,只能在类“Player”中访问
  }
}


const elton = new Player('Elton', 'Steele')
elton.fullName
elton.score
elton.score = 99

使用private修饰的属性或方法,在子类中也是不允许访问,有什么方法能够保证属性即是私有,同时也能在子类中能够被访问?

可以使用protected关键字。

class Player {
  first: string
  last: string
  protected _score: number

  constructor(first: string, last: string) {
    this.first = first
    this.last = last
  }

  get fullName(): string {
    return `${this.first} ${this.last}`
  }

  get score(): number {
    return this._score
  }

  set score(newScore: number) {
    this._score = newScore
  }
}

class SuperPlayer extends Player {
  public isAdmin: boolean = true

  maxScore() {
    this._score = 999999
  }
}

const elton = new SuperPlayer('Elton', 'Steele')

elton.maxScore()

Classes and Interface

实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。这个特性大大提高了面向对象的灵活性。 ———— 类实现接口

interface Colorful {
  color: string
}

class waterMelon implements Colorful {
  color = 'green'
}

class strawBerry implements Colorful {
  color = 'red'
}

也可以implements多个接口

interface Colorful {
  color: string
}
interface Printable {
  print(): void
}

class waterMelon implements Colorful, Printable {
  color = 'green'
  print() {}
}

Abstract Classes

abstract 用于定义抽象类和其中的抽象方法。

抽象类,不能被实例化的的类

abstract class Cat {}

new Cat() // ❌ 无法创建抽象类的实例

不能被实例的类有什么意义呢?

实际上,抽象类更像是定义了一种模式,定义了必须由子类实现的方法。

abstract class Employee {
  first: string
  last: string

  constructor(first, last) {
    this.first = first
    this.last = last
  }

  abstract getPay(): number // 表示getPay方法需要存在于任何继承了Employee的类中
}

是不是有点像interface的作用,但不同的是,抽象类还可以在内部定义真实可用的方法。

abstract class Employee {
  first: string
  last: string

  constructor(first, last) {
    this.first = first
    this.last = last
  }

  abstract getPay(): number // 表示getPay方法需要存在于任何继承了Employee的类中

  print() {
    console.log('hello')
  }
}

实现抽象类

abstract class Employee {
  first: string
  last: string

  constructor(first, last) {
    this.first = first
    this.last = last
  }

  abstract getPay(): number // 表示getPay方法需要存在于任何继承了Employee的类中

  print() {
    console.log('hello')
  }
}

class FullTime extends Employee {
  getPay(): number {
    return 1
  }
}

class PartTime extends Employee {
  getPay(): number {
    return 0.5
  }
}

以上两个子类都必须实现getPay方法

我们可以继续拓展子类

abstract class Employee {
  first: string
  last: string

  constructor(first, last) {
    this.first = first
    this.last = last
  }

  abstract getPay(): number // 表示getPay方法需要存在于任何继承了Employee的类中
}

// 全职
class FullTime extends Employee {
  constructor(
    first: string,
    last: string,
    private salary: number
  ) {
    super(first, last) // 公共继承的属性
  }

  getPay(): number {
    return this.salary
  }
}
// 实习
class PartTime extends Employee {
  constructor(
    first: string,
    last: string,
    private hourlyRate: number,
    private hoursWorked: number
  ) {·
    super(first, last) // 公共继承的属性
  }

  getPay(): number {
    return this.hourlyRate * this.hoursWorked
  }
}

const betty = new FullTime('Betty', 'White', 95000)
const bill = new PartTime('Bill', 'Willington', 24, 72)

betty.getPay()
bill.getPay()