I have the following type definitions:
interface DefaultConfig {
state: {
initialState: AllState;
};
}
type UserConfig = {
[K in keyof DefaultConfig]: DefaultConfig[K];
};
// Could also use 'type UserConfig = Partial<DefaultConfig>;'
interface AssignDefaults {
(userConfig: UserConfig): DefaultConfig;
}
I use these in the following function:
const defaultConfig: DefaultConfig = {
state: {
initialState: defaultInitialState
}
};
const assignDefaults: AssignDefaults = userConfig => {
const thisUserConfig = userConfig;
Object.keys(userConfig).forEach(key => {
if (key in defaultConfig) {
const maybeObject = userConfig[key];
if (!!maybeObject && maybeObject.constructor === Object) {
thisUserConfig[key] = {
...(defaultConfig[key] || {}),
...userConfig[key]
};
}
}
});
return { ...defaultConfig, ...thisUserConfig };
};
What I expect to happen is that userConfig[key] should be equal to keyof DefaultConfig, as that's what is defined in the UserConfig index signature.
However, this doesn't work as Object.keys(...) defines its return value as string[]. This causes a Typescript error:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'UserConfig'.
No index signature with a parameter of type 'string' was found on type 'UserConfig'.
It seems Typescript is unable to determine that keyof DefaultConfig is also a string.
I know I can remove this error by including [key: string]: any to the DefaultConfig interface, however I would much prefer not to do this as:
- These are the only keys and values allowed for the object it represents (e.g. the
defaultConfigobject should not containfoo: "bar"as a key/value pair, however this would be allowed ifDefaultConfigincluded the index signature. - When I need to call
defaultConfig.state.initialState, I don't want it to return the type asany, which is what happens if that index signature is included. Same with if I create a constconst foo = assignDefaults(userConfigObject)then callfoo.state.initialState. - I don't like using
anyin my type definitions unless absolutely necessary.
Is this a limitation of Typescript or is there some workaround for this?