Issue
I would like to achieve something like this:
export interface GCLPluginConfig {
[key: string]: string | number | boolean | Date | string[];
}
export interface CorePluginConfig extends GCLPluginConfig {
"core.lastUpdateCheck": Date;
}
export interface AnsibleConfig extends GCLPluginConfig {
"ansible.workingFolders": string[];
"ansible.defaultHost": string;
"ansible.checkedAnsibleIntall": boolean;
}
public setConfigValue<ConfigType extends GCLPluginConfig>(
key: keyof ConfigType,
value: ConfigType[keyof ConfigType]
) {
const config = this.getConfig();
config[key as string] = value;
this.saveConfig(config);
}
And use it this way:
this.configService.setConfigValue<AnsibleConfig>( 'ansible.workingFolders', workingFolders );
I want the compiler to show an error then setConfigValue got a key which is not keyof type parameter or the value parameter in this case workingFolders doesnt has the same type as the property defined in AnsibleConfig with this key.
Can you help me? I didnt find any working solution for this. Thanks! 🙏
Solution
You actually don't want the index signature in your config subtypes here. They have specific properties, and any other properties should not exist at all.
So you need to drop the extends GCLPluginConfig
since that will bring in the index signature we don't want.
interface GCLPluginConfig {
[key: string]: string | number | boolean | Date | string[]
}
export interface CorePluginConfig {
"core.lastUpdateCheck": Date;
}
export interface AnsibleConfig {
"ansible.workingFolders": string[];
"ansible.defaultHost": string;
"ansible.checkedAnsibleIntall": boolean;
}
But now you will get this error:
new ConfigService().setConfigValue<AnsibleConfig>('ansible.workingFolders', ['a']);
// Type 'AnsibleConfig' does not satisfy the constraint 'GCLPluginConfig'.
// Index signature for type 'string' is missing in type 'AnsibleConfig'.(2344)
One gotcha of interfaces vs types is that interfaces do not have implicit index signatures, but types do. So if you want to use a constraint type that has an index signature, then it's best to use a type
instead. Converting those interface
s to type
s fixes this issue:
type GCLPluginConfig = {
[key: string]: string | number | boolean | Date | string[]
}
export type CorePluginConfig = {
"core.lastUpdateCheck": Date;
}
export type AnsibleConfig = {
"ansible.workingFolders": string[];
"ansible.defaultHost": string;
"ansible.checkedAnsibleIntall": boolean;
}
new ConfigService().setConfigValue<AnsibleConfig>('ansible.workingFolders', ['a']); // fine
And this gives the appropriate errors:
new ConfigService().setConfigValue<AnsibleConfig>('ansible.workingFolders', 123); // error
new ConfigService().setConfigValue<AnsibleConfig>('foo', 123); // error
This is one of a few reasons it's usually recommended to use type
by default, and only change it to interface
when you want interface
specific features.
But now it may be too easy to create invalid config types:
export type FooConfig = { a: () => void }
// fine, but function should not be allowed
This will error when you try use it, since it doesn't confirm to GCLPluginConfig
:
new ConfigService().setConfigValue<FooConfig>('a', () => undefined); // error
But maybe you want to catch that type error when you define the config type.
If so, you can make a type factory that applies this constraint.
type GCLPluginConfigConstraint = {
[key: string]: string | number | boolean | Date | string[]
}
type GCLPluginConfig<
T extends GCLPluginConfigConstraint = GCLPluginConfigConstraint
> = T
And now use that create your config types:
export type CorePluginConfig = GCLPluginConfig<{
"core.lastUpdateCheck": Date;
}>
export type AnsibleConfig = GCLPluginConfig<{
"ansible.workingFolders": string[];
"ansible.defaultHost": string;
"ansible.checkedAnsibleIntall": boolean;
}>
And if you create a bad config type here, you get an appropriate error:
export type FooConfig = GCLPluginConfig<{ a: () => void }> // error
Taking this extra step may or may not be something you need. It's up to you.
Answered By - Alex Wayne
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.