ts中class的访问器,索引签名,implenets, extends,super与ts子类父类关系

116 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

ts class支持功能

访问器getters/setters

ts class同样支持创建访问器,如

class C {
  private _length = 0
  get length() {
    return this._length
  }
  set length(value) {
    this._length = value
  }
}
const c = new C()
c.length = 1
console.log(c.length)

上述代码展示了class访问器的一个基本用法,但没有实际的意义,我们使用访问器应该是想隐藏原始属性的访问,并且具备一些其他的逻辑。

访问器的一些规则

  1. 如果只有get方法而没有set方法,该属性默认是只读的。如
class C {
  private _length = 0
  get length() {
    return this._length
  }
  
}
const c = new C()
c.length = 1 //Cannot assign to 'length' because it is a read-only property
console.log(c.length)
  1. 如果set方法的传参类型未指定,则其传参类型默认会是get方法的返回类型(ts4.3之后允许setters的传参类型与getters的返回类型不同),如
class C {
  private _length = 0
  get length() {
    return this._length
  }

  set length(value){ //这里没指定类型,默认为get返回类型,即number
    this._length = value
  }
  
}
const c = new C()
c.length = 'str' //Type 'string' is not assignable to type 'number'.
  1. getters和setters必须具备相同的可访问性(实际测试,应是getters至少比setters具备更大的可访问性),如
class C {
  private _length = 0
  private get length() {// A get accessor must be at least as accessible as the setter(
    return this._length
  }

  set length(value){ // A get accessor must be at least as accessible as the setter(
    this._length = value
  }
  
}
 

索引签名(Index Signature)。在类上使用索引签名并不常见,因为类上的方法同样需要指定出类型

class中同样可以创建索引签名,索引签名允许我们创建预设类型,且不限制数量的属性。如

class C {
 [pName: string] : boolean | string

 someNumPisNotAllowed = 1 // Property 'someNumPisNotAllowed' of type 'number' is not assignable to 'string' index type 'string | boolean'.
}

implements 子句

implements关键字允许类实现某接口,这样我们可以限制类必须具备接口的一些功能,如

interface People {
  name: string
  age: number, 
}
interface Fun {
  run: () => void
}
class JoeZhou implements People,Fun{
  age = 18 // Property 'name' is missing in type 'JoeZhou' but required in type 'People'
  run(){
    console.log('I can run')
  }
}

使用implemtns子句应注意,实现了某接口的类的方法和属性类型不会因为接口而改变,ts只会进行一个类型检查,如

interface People {
  age: number, 
}
interface Fun {
  run: (name: string) => void
}
class JoeZhou implements People,Fun{
  age = 18 
  run(name){ //这里的name为any,假如取消了ts的假如取消了noImplicityAny规则,则不会报错
    console.log('I can run')
  }
}
const JZ = new JoeZhou();
JZ.run(1)

extends子句

ts中一个类(A)可以继承另一个类的属性和方法,并且类A可以拥有自己的属性和方法,且可以重写继承的类的方法和属性,如

class BaseC {
  echo(){
   console.log('BaseC')
  }
  baseM(){
    console.log('I am method of BaseC')
  }
}

class AnotherC extends BaseC {
  echo(){
   console.log('AnotherC')
  }
   aCMethod(){
    console.log('I am method of AnotherC')
   }
}

const ac = new AnotherC()
ac.aCMethod()// I am method of AnotherC
ac.baseM()// I am method of BaseC
ac.echo()// AnotherC

super关键字

在子类(衍生类)中可以使用super关键字访问父类(基类)的方法和属性,如

class BaseC {
  echo(){
   console.log('BaseC')
  }
}

class AnotherC extends BaseC {
  echo(flag?: 'base' | undefined){
    if(flag === 'base'){
      super.echo()
    }else{
      console.log("AnotherC")
    }
  }
}

const ac = new AnotherC()
ac.echo('base')// BaseC

ts子类和父类的关系

ts会强制子类的类型为其父类的子类型,并且要求遵循父类的约束条件,所以

  1. 可以用父类类型接收子类,如
Class Base {}
Class Child extends Base {}
const b:Base = new Child()
  1. 子类重写父类方法应遵循父类方法的规则,如上面super关键字的例子,子类中echo方法中flag后的?不可以去掉,因为父类的echo方法并不接收参数。

子类与父类的初始化顺序

  1. 父类的类属性默认初始化
  2. 父类运行constructor函数
  3. 子类的类属性默认初始化
  4. 子类运行constructor函数

子类声明更具体的类属性,考虑如下例子

interface Animal {
   dateofbirth: string
}
interface Dog extends Animal{
  breed: string
}
class AnimalHouse {
  resident: Animal
  constructor(animal:Animal){
    this.resident = animal
  }
}

class AnimalImp implements Animal{
  dateofbirth = 'birthday'
}

class DogHouse extends AnimalHouse{
  
}

const dh = new DogHouse(new AnimalImp())

我们创建了AnimalHouse的子类DogHouse,我们让AnimalHouse为我们创建resident属性。但我们发现在DogHouse中的resident是AnimalHouse的实例,我们想要更具体点,在DogHouse中的resident应该为Dog类型,但又不想让DogHouse自己创建该属性。则我们可以借助declare关键字,如将DogHouse改成如下代码

class DogHouse extends AnimalHouse{
  declare resident:Dog
}

这样ts不会要求我们在DogHouse类中初始化该resident,而只是声明了DogHouse中resident的类型。