DEV Community

Cover image for TypeScript: Creating a Dynamic Interface
bob.ts
bob.ts

Posted on

TypeScript: Creating a Dynamic Interface

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;
}
Enter fullscreen mode Exit fullscreen mode

... 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;
}
Enter fullscreen mode Exit fullscreen mode

... 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;
};
Enter fullscreen mode Exit fullscreen mode

The Union Approach

The following code is another resolution.

export interface ConfigType {
  type: string;
}

export interface DataModel {
  config: ConfigType;
  [key: string]: string | ConfigType;
}
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
mondal10 profile image
Amit Mondal

Thanks, this saved my day 🙌

Collapse
 
rfornal profile image
bob.ts

Glad it helped!

Collapse
 
nirekcuf profile image
nirekcuf • Edited

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}>

Collapse
 
jansendev profile image
JhonatanSegura Galindo

Thanks 👌