基于同一对象中的键,可以缩小属性的可能值

时间:2019-08-28 08:02:45

标签: typescript

我具有以下结构:

export interface AppConfig {
  encryptionKey: string;
  db: TypeOrmModuleOptions;
}

export interface BrandsConfig {
  /**
   * Brand name
   */
  [key: string]: AppConfig;
}

export interface IConfig {
  brands: BrandsConfig;
  master: string;
}

export class Config implements IConfig {
  public readonly brands: BrandsConfig;
  public readonly master: string;
  public constructor(init: IConfig) {
    Object.assign(this, init);
  }
}

这个想法是BrandsConfig是一个键/值集合,其中值是此命名实例的设置。同时,“主人”应该赋予其中一个(只有一个)特殊身份。

有什么方法可以静态地限制“ master”的可能值(至少在Config类中,理想情况下也在接口中),以便它们不仅是任何字符串,而且是位于BrandConfig密钥?我知道我可以在构造函数中的运行时执行此检查,但是我正在尝试将尽可能多的检查移到编译阶段,并为此提供适当的智能感知。

1 个答案:

答案 0 :(得分:1)

您可以使用Genericskeyof/Index Type Query operator来做到这一点。

1。)将通用类型参数B添加到您的Config类中,该参数可分配给/扩展BrandConfig:

export class Config<B extends BrandsConfig> implements IConfig<B> {
  public readonly brands: B;

  public readonly master: keyof B;

  public constructor(init: IConfig<B>) {
    // Make it compile. Feel free to replace it by delegates, getters,etc.
    this.brands = init.brands;
    this.master = init.master;
  }
}

2。)还要在IConfig中添加相应的泛型类型参数,因此我们可以将master定义为依赖于BrandsConfig中的键的键:

export interface IConfig<B extends BrandsConfig> {
  brands: B;
  master: keyof B;
}

让我们测试客户端。如果我们为master传递了错误的密钥,那么在这里我们将期望一个错误。

const myConfig = {
  brands: {
    brand1: { encryptionKey: "", db: {} },
    brand2: { encryptionKey: "", db: {} }
  },
  master: "brand1"
} as const

const config = new Config(myConfig); // works!

现在将您的主属性更改为master: "Wanna be a brand",然后您将获得:

  

类型'“想要成为品牌”'不能分配给类型'“品牌1” | “ brand2””。

还请注意,我们使用const assertions来缩小myConfig的推断类型。

Playground