【问题标题】:Typescript: Shortening Simple Class Declaration打字稿:缩短简单的类声明
【发布时间】:2022-01-14 17:54:52
【问题描述】:

我有一个我觉得非常有用的 TS 代码模式,但它非常不DRY。任何想法如何解决它?

我喜欢将 TS interfaceclass 声明“链接”在一起。通过这种方式,我有一些简单的类型化数据结构和继承的优势,并且我可以在运行时使用instanceof 运算符轻松进行类型检查(这使我可以避免type predicates,我认为这是不安全的)。

有关示例,请参见下面的代码,其中我有一个类 Doctor,它扩展了一个基类 Person

interface PersonInterface {
  id: number
  name: string
  surname: string
}
class Person implements PersonInterface {
  id: number
  name: string
  surname: string

  constructor(arg: PersonInterface) {
    this.id = arg.id
    this.name = arg.name
    this.surname = arg.surname
  }
}

interface DoctorInterface extends PersonInterface {
  degree: string
  salary: number
}
class Doctor extends Person implements DoctorInterface {
  degree: string
  salary: number

  constructor(arg: DoctorInterface) {
    super(arg)
    this.degree = arg.degree
    this.salary = arg.salary
  }
}

const doc = new Doctor({
  id: 111,
  name: 'John',
  surname: 'Johnson',
  degree: 'PHD',
  salary: 100000,
})

console.log(doc instanceof Person) // true
console.log(doc instanceof Doctor) // true

一切正常,类型检查很简单,我的 IntelliSense 很开心。都很棒。但正如你所看到的,我每节课重复自己 3 次。一次声明interface,另一次在class 中实现它,最后一次应用constructor

没有更简洁的方法吗?在一个大项目中,这看起来很可怕。

重要 我不需要在我的类中有方法。它们仅用于呈现数据,而不是行为(实际上我正在使用它们来填充 vuex 存储)

【问题讨论】:

  • 将对象传递给字典有多重要?在某些情况下,您也许可以使用parameter properties 来减少重复。
  • 我尝试了不同的方式,但我还没有找到一种可以很好地处理继承的方法...... TS 会抱怨很多关于接口没有正确实现或属性没有正确初始化的问题。在某些情况下,它可以工作,但是当我实例化该类时,TS 并不知道所有属性,访问它们会产生更多错误。
  • 有一种方法可以使用对象in this SO answer,如果您愿意使用Object.assign 并可能复制所有可枚举的属性,而不仅仅是命名的三个或多个属性。该答案还链接到issue #5326,这是您想要但尚未实现的。

标签: typescript class design-patterns interface dry


【解决方案1】:

首先,没有什么神奇的语法可以让它变得更好。

但值得注意的是,一个类可以用作接口:

class Person {
  id: number
  name: string
  surname: string

  constructor(arg: Person) {
    this.id = arg.id
    this.name = arg.name
    this.surname = arg.surname
  }
}

// works
const personData: Person = { id: 123, name: 'Al', surname: 'Bundy' }
const person: Person = new Person(personData)

你可能觉得这有点奇怪,但是普通对象和Person 的实例都具有完全相同的公共接口,因此它们被认为是兼容的。


重要提示我不需要在我的类中有方法。它们仅用于呈现数据,而不是行为(实际上我正在使用它们来填充 vuex 存储)

那么你为什么要使用类呢?那么就没有必要了。符合该接口的普通对象与实现该接口的类的实例或多或少无法区分,因此您可以节省大量样板文件:

interface Person {
  id: number
  name: string
  surname: string
}

const person: Person = { id: 123, name: 'Al', surname: 'Bundy' }

【讨论】:

  • 嘿韦恩,你的答案可能是好的,但我还是会等一下,以防有一些魔术师对我正在寻找的东西有一些晦涩的特殊语法。已经使用类作为接口正在使事情变得更好。谢谢。我仍然想使用类而不是普通对象,所以我可以将instanceof 作为类型narrowing operator。我发现它可读且健壮...比type predicates.
  • 另一个选项是discriminated unions。只需给所有接口一个通用类型字段和if (myData.type === 'person') { ... }
  • 这是一个很好的建议!它确实满足了我的需求。谢谢。
猜你喜欢
  • 2019-02-16
  • 1970-01-01
  • 2018-11-06
  • 1970-01-01
  • 2017-08-12
  • 2021-01-20
  • 2020-09-12
  • 2022-01-06
  • 2019-03-26
相关资源
最近更新 更多