首页 > 解决方案 > 打字稿:将属性添加到类中以替代类型

问题描述

我有一个类型是字符串的析取:

export type Category = 'people' | 'projects' | 'topics' | 'tools'

我将索引声明为:

interface Entry {
  ...
}
type IPostEntryIndex = {
  [name in Category]: Entry
}

如何声明与类别名称对应的类属性?

const categories: Category[] = ['people', 'projects', 'topics', 'tools']

class PostEntryIndex implements IPostEntryIndex {
  // what goes here so that `PostEntryIndex` has an attribute
  // for each category in Category?

  constructor () {
    categories.map(cat => this[cat] = new Entry())
  }
}

更新:当然,我可以简单地明确声明类别。我正在寻找的是一个 DRY 答案,如果我添加一个类别,则不需要更新。(注意使categories数组不重复Category也很好。我想我可以为此使用枚举。)

标签: typescript

解决方案


据我所知,没有在一个类中声明一堆属性的语法。您可以通过这种方式添加索引签名,但不能添加字符串文字的联合。显然您可以手动添加它们,但我认为重点是您不想这样做。

可以做的一件事是使用声明合并将属性添加到类实例的接口中。当你编写class PostEntryIndex {}它时,它会创建一个名为的值PostEntryIndex,它是一个类构造器,以及一个名为对应于类索引的接口。PostEntryIndex你可以使用接口合并来添加你想要的任何属性......像这样:

interface PostEntryIndex extends IPostEntryIndex { }
class PostEntryIndex {
  constructor() {
    categories.map(cat => this[cat] = new Entry())
  }
}

请注意,这样做会绕过模式附带的严格的类属性初始化检查--strict,因此您不能依赖编译器来抱怨未初始化的属性。(这实际上是为了您的优势,因为编译器不理解循环categories用于初始化所有相关属性。)

另请注意,一旦以这种方式合并接口,该implements子句就特别多余,因此我将其删除。(事实证明,implements子句从来都不是真正必要的;TypeScript 的结构类型系统不需要您声明A implements B使用Aas a B,只要它们具有兼容的结构......但我离题了)

无论如何,让我们看看它是否有效:

const pei = new PostEntryIndex();
pei.projects; // Entry

看起来不错。好的,希望有帮助;祝你好运!

Playground 代码链接


推荐阅读