重学TS --- class

574 阅读5分钟

接口和类

类实现接口

这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战

基本使用

简单使用

class Person {
  // 这里定义的是实例中的name和age的类型
  name: string
  age: number

  // 这里定义的是构造函数参数的数据类型
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

继承

class Person {
  name: string
  age: number

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

class Student extends Person {
  sno: string

  constructor(name: string, age: number, sno: string) {
    super(name, age)
    this.sno = sno
  }
}

修饰符

public公共的,在子类和类定义外部都可以使用缺省值
protected在子类中可以访问,在类定义外部无法访问
private私有的,只能在类的内部进行使用,类的外部和子类中都无法访问
readonly定义只读属性

public

class Parent {
  // 不写默认就是public
  name: string

  constructor(name: string) {
    // public属性可以在类自身被访问
    this.name = name
  }
}

class Child extends Parent{
  constructor(name: string) {
    super(name)
  }

  printName() {
    // public属性可以在子类中被访问
    console.log(this.name)
  }
}

const childInstance = new Child('Klaus')

// public属性可以在类定义外部被访问
console.log(childInstance.name) // => Klaus
childInstance.printName() // => Klaus

protected

class Parent {
  protected name: string

  constructor(name: string) {
    // private属性可以在类自身中被使用
    this.name = name
  }
}

class Child extends Parent{
  constructor(name: string) {
    super(name)
  }

  printName() {
    // protected属性可以在子类中被访问
    console.log(this.name)
  }
}

const childInstance = new Child('Klaus')

// protected属性不可以在类的定义外部被访问
// console.log(childInstance.name) // error
childInstance.printName() // => Klaus
class Parent {
  protected name: string

  // 当使用protected修饰符限制构造函数
  // 表明这个类只能被继承不能被实例化
  protected constructor(name: string) {
    this.name = name
  }
}

class Child extends Parent{
  constructor(name: string) {
    super(name)
  }

  printName() {
    console.log(this.name)
  }
}

// console.log(new Parent('Alex')) // error

private

class Parent {
  private name: string

  constructor(name: string) {
    // private属性可以在类自身中被使用
    this.name = name
  }
}

class Child extends Parent{
  constructor(name: string) {
    super(name)
  }

  printName() {
    // private属性不可以在子类中被访问
    // console.log(this.name) // error
  }
}

const childInstance = new Child('Klaus')

// protected属性不可以在类的定义外部被访问
// console.log(childInstance.name) // error
class Parent {
  private name = 'Klaus'
}

const per = new Parent()
// TS只是编译的时候,对代码的类型进行静态分析,并不会实际执行代码
// 所以TS中的私有属性,只是尽可能阻止你使用该属性
// 并不是将这个属性真正变成私有的了
console.log(per) // =>  { name: 'Klaus' }

readonly

class Parent {
  // name属性是只读属性
  readonly name: string

  constructor(name: string) {
    this.name = name
  }
}

const instance = new Parent('Klaus')

// 使用readonly修饰的属性是只读属性,无法被修改
// instance.name = 'Alex' // error

参数属性

class Parent {
  // 在这里对实例属性进行修饰
  protected readonly name: string

  constructor(name: string) {
    this.name = name
  }
}

const instance = new Parent('Klaus')

但是在TS中,类的属性修饰和定义其实是可以使用语法糖放置到一起的,而这个语法糖就被称之为参数属性

class Parent {
  // 定义参数属性
  constructor(protected readonly name: string) {
    this.name = name
  }
}

const instance = new Parent('Klaus')

静态属性

class Parent {
  // 定义公共的静态属性username
  public static username = 'Klaus'
}

console.log(Parent.username) // => Klaus

可选属性

class Parent {
  name: string // name是必传属性
  age?: number = 18 // age是可选属性 默认值是18

  // uid属性是可选的 默认值是undefined
  constructor(name: string, age?: number, public uid?: string) {
    this.name = name

    // 这里其实是显示的将undefined赋值给了实例属性age
    // 所以在输出结果的时候,age的属性值为undefined
    this.age = age
  }
}

console.log(new Parent('Klaus')) // => { uid: undefined, age: undefined, name: 'Klaus' }

存取器

class Person {
  private _name: string = ''

  get name() {
    return this._name
  }

  set name(value: string) {
    this._name = value
  }
}

const per = new Person()
per.name =  'Klaus'
console.log(per.name) // => Klaus

抽象类

抽象类只能被子类继承,不能被实例化

抽象类可以用来约束子类中必须存在的属性或方法

一个抽象类中可以有抽象属性和抽象方法,也可以同时存在存在实现的属性和方法

一个抽象类可以继承自另一个抽象类,此时父抽象类的抽象方法或抽象属性可以不在子抽象类中实现

abstract class Parent {
  // 抽象方法定义格式
  // abstract [属性描述符](参数列表): 返回值类型
  abstract print(value?: string): void
}

// 抽象子类可以不实现也可以实现抽象父类中的抽象属性或方法
abstract class Child extends Parent {
  pirntChildName() {
    console.log(Child.name)
  }
}

// 如果一个类继承自抽象类,则该类必须实现抽象类中的属性和方法
class Instance extends Child {
  print(value: string) {
    console.log(value)
  }
}

const inst = new Instance()
inst.print('Hello TypeScript')
inst.pirntChildName()
abstract class Parent {
  abstract print(value?: string): void
  abstract age: number
  // 在类中(包括抽象类)如果一个私有属性没有在构造函数中被赋值
  // 那么就一定需要被初始化
  private _name:string = ''
  abstract get name(): string
  // 特别的: 对于属性访问器在定义类型的时候
  // 不可以添加返回值的类型标注
  abstract set name(value: string)
}

class Child extends Parent {
  name: string
  age: number

  constructor(name: string, age: number) {
    super()
    this.name = name
    this.age = age
  }

  print(value: string) {
    console.log(value)
  }
}

类类型

class Person {
  name: string

  constructor(name: string) {
    this.name = name
  }
}

// 此时per的类型就是Person
// 即Person即是类型又是值
let per = new Person('Klaus')

class Animal extends Person {
  age: number

  constructor(name: string, age: number) {
    super(name)
    this.age = age
  }
}

// per的类型是Person
// Animal是Person子类的类型
// 所以符合类型兼容性,可以被赋值
per = new Animal('Dog', 8)

类实现接口

我们可以让一个类实现某个接口,从而通过该接口来约束对应的实例结构

interface IPerson {
  name: string
}

class Person implements IPerson {
  name: string = ''
}

接口继承类

class Person {
  name: string = ''
}

interface IFoo extends Person {}
// 等价于
/*
  interface IFoo {
    name: string
  }
*/

const foo: IFoo = {
 name: 'Klaus'
}
class Person {
  protected name: string = ''
}

// 类中的受保护属性也会被类继承实现
// 但此时这个接口就只能给这个类本身或该类的子类所使用
interface IFoo extends Person {}

class Foo extends Person implements IFoo {}