考虑下面的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编译器的运行时检查?
在将不能作为类型守卫来断言一个字符串是一个类型的键。你可以创建一个自定义的类型守卫,来执行窄化。
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
但同样的道理在这里也适用。虽然在一般情况下它不是类型安全的,但你仍然可以在指定的注意事项下使用它。