During my time as a developer I've created a lot of "typed" code. One particular issue has come up repeatedly and I finally had a moment of clarity.
The Problem
Whenever I write something like this ...
export interface ConfigType {
type: string;
}
export interface DataModel {
config: ConfigType; // ERROR HERE
[key: string]: string;
}
... I get the following error on the commented line above.
Property 'config' of type 'ConfigType' is not assignable to 'string' index type 'string'.
The issue is that the [key: string]: string;
line gets enforced on all key/value pairs on the interface.
I've seen something like the following ...
export interface Person {
id: number;
firstname: string;
lastname: string;
[key: string]: string | number;
}
... and this code does not present an error. This is because the[key: string]: string;
line gets enforced on all key/value pairs on the interface and they are string
or number
.
I came up with two approaches to solving the issue listed below. I think the first is the better approach, but I will list both for consistency.
The Type Approach
This approach seems much cleaner, creating a new data type that has the fixed and dynamic portions ANDed together.
export interface ConfigType {
type: string;
}
export type DataModel = {
config: ConfigType;
} & {
[key: string]: string;
};
The Union Approach
The following code is another resolution.
export interface ConfigType {
type: string;
}
export interface DataModel {
config: ConfigType;
[key: string]: string | ConfigType;
}
This approach has the "issue" that a new "key" could be used with another ConfigType
.
Conclusion
As I said, I think the first approach (the Type Approach) is the better of the two.
If anyone has another or better pattern, please let me know.
Top comments (4)
Thanks, this saved my day 🙌
Glad it helped!
Not sure if this is what your going for, but I did something like
type Implements< T, R extends T > = R;
then
type myInterface= Implements<{ [key: string]: unknown }, {config:ConfigType}>
Thanks 👌