Consider the following TypeScript code snippet, which compiles and works successfully:
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;
}
};
However, this code sucks, because by specifying [key: string]: boolean
in the class, we lose the class safety. In other words, the following code will compile successfully but give a runtime error:
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. So can I remove the [key: string]: boolean
in the class definition? Let's try and see what happens:
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;
}
};
This code does not satisfy the TypeScript compiler. It fails on the value !== settings[setting]
part, throwing the error of:
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? How can I construct a runtime check that satisfies the TypeScript compiler?