限制成员只接受扩展或实现其他类型的类型(不是实例)[TypeScript]

时间:2018-10-02 02:14:06

标签: typescript interface abstraction

我需要一些有关打字稿中抽象的帮助。

我想限制成员只接受类型作为值,但是这些类型需要实现或扩展其他类型或接口。

例如。

以下示例不代表真实数据模型或与之相关,而是说明目标的示例。

interface Profession {
  work()
}

class Engineer implements Profession {
  work() {...}
}

class Doctor  {
  work() {...}
}

interface ProfessionRankingMap {
  top1ProfessionType: // Here is where I don't know how to constraint
  top2ProfessionType: // Here is where I don't know how to constraint
}

const classAProfessionRankingMap: ProfessionRankingMap {
  // This is okay, Type Engineer implements Profession interface
  top1ProfessionType: Engineer
  // This is bad, Type Doctor doesn't implement Profession interface
  top2ProfessionType: Doctor
}

const classBProfessionRankingMap: ProfessionRankingMap {
  // This is bad, [new Engineer()] returns an instance not a type
  top1ProfessionType: new Engineer()
  // This is bad, [new Doctor()] returns an instance not a type
  top2ProfessionType: new Doctor()
}

1 个答案:

答案 0 :(得分:1)

要以类型表示类,您需要构造函数签名:

interface ProfessionRankingMap {
  // The implementer must have a constructor with no args that returns Profession 
  top1ProfessionType: new () => Profession 
  top2ProfessionType: new () => Profession
}

const classAProfessionRankingMap: ProfessionRankingMap = {
  top1ProfessionType: Engineer,
  top2ProfessionType: Doctor
}

Playground link

如果我们想接受具有任意数量参数的构造函数的类,则可以使用new (... args:any[]) => Profession

问题的第二部分要复杂一些。 Typescript使用结构化类型确定类型兼容性。因此,类的结构与implements子句无关。 implements子句只会在声明类时为您提供错误,并确保它具有所有必需的成员,因此您尽早获得错误,而不是在尝试使用需要接口的类时提早得到错误。

这意味着,只要Doctor拥有Profession的所有成员,就无法阻止它在期望的Profession处传递。

唯一的解决方案是对私有成员使用抽象类。私有成员将确保除扩展抽象类的类之外的其他任何类均不与抽象类类型兼容:

abstract class Profession {
  private _notStructural: undefined;
  abstract work(): void
}

class Engineer extends Profession {
    work() { }
}

class Doctor  {
    work() { }
}

interface ProfessionRankingMap {
  top1ProfessionType: new () => Profession
  top2ProfessionType: new () => Profession
}

const classAProfessionRankingMap: ProfessionRankingMap = {
  top1ProfessionType: Engineer,
  top2ProfessionType: Doctor // error
}

Playground link