TypeScript - 如何在不失去类安全的情况下通过字符串索引一个类?

问题描述 投票:0回答:1

考虑下面的TypeScript代码片段,它成功地编译和工作。

class UserSettings {
  [key: string]: boolean;

  setting1: boolean = false;
  setting2: boolean = false;
  setting3: boolean = false;
  otherOtherSetting: boolean = true;
}

const settings = new UserSettings();

const checkSettingChanged = (setting: string, value: boolean) => {
  if (value !== settings[setting]) {
    console.log(`The setting of ${setting} changed, updating it!`);
    settings[setting] = value;
  }
};

然而,这段代码很糟糕,因为通过指定了 [key: string]: boolean 的类中,我们失去了类的安全性。换句话说,下面的代码会编译成功,但会给出一个运行时错误。

function someOtherFunction() {
  // Oops, I forgot to specify setting 4 in the class definition above,
  // so this code should fail to compile, but TypeScript doesn't care,
  // and now I will get a stupid runtime error
  if (settings.setting4) {
    console.log('Setting 4 is enabled!');
  }
}

Ok. 那么我是否可以删除 [key: string]: boolean 的类定义中?让我们试着看看会发生什么。

class UserSettings {
  // This is just a "normal" TypeScript class now
  setting1: boolean = false;
  setting2: boolean = false;
  setting3: boolean = false;
  otherOtherSetting: boolean = true;
}

const settings = new UserSettings();

const checkSettingChanged = (setting: string, value: boolean) => {
  // Hopefully, adding this runtime check should satisfy the TypeScript compiler
  if (!(setting in settings)) {
    throw new Error(`The setting of ${setting} does not exist in the UserSettings class.`);
  }

  if (value !== settings[setting]) {
    console.log(`The setting of ${setting} changed, updating it!`);
    settings[setting] = value;
  }
};

这段代码 满足TypeScript编译器的要求。它的失败在于 value !== settings[setting] 部分,抛出错误的。

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'UserSettings'.
  No index signature with a parameter of type 'string' was found on type 'UserSettings'.

What gives? 如何构造一个满足TypeScript编译器的运行时检查?

typescript
1个回答
1
投票

在将不能作为类型守卫来断言一个字符串是一个类型的键。你可以创建一个自定义的类型守卫,来执行窄化。

class UserSettings {
  // This is just a "normal" TypeScript class now
  setting1: boolean = false;
  setting2: boolean = false;
  setting3: boolean = false;
  otherOtherSetting: boolean = true;
}

const settings = new UserSettings();
function isKeyOf<T>(p: PropertyKey, target: T): p is keyof T {
    return p in target;
}

const checkSettingChanged = (setting: string, value: boolean) => {
  // Hopefully, adding this runtime check should satisfy the TypeScript compiler
  if (!isKeyOf(setting, settings)) {
    throw new Error(`The setting of ${setting} does not exist in the UserSettings class.`);
  }

  if (value !== settings[setting]) {
    console.log(`The setting of ${setting} changed, updating it!`);
    settings[setting] = value;
  }
};

[游乐场链接](游乐场链接)

然而并不是说这不是完全的类型安全。基本的OOP规定,你应该能够有在 settings 衍生的类的实例 UserSettings,它可能没有所有的布尔值。你可以阅读更多关于这个问题 此处 因为它适用于 Object.keys但同样的道理在这里也适用。虽然在一般情况下它不是类型安全的,但你仍然可以在指定的注意事项下使用它。

© www.soinside.com 2019 - 2024. All rights reserved.