Home > Enterprise >  How to define an interface that accepts zero or more dynamically named attributes in Typescript
How to define an interface that accepts zero or more dynamically named attributes in Typescript

Time:02-02

I have an interface that looks like this:

export interface ReportingNodeConfig {
  template: string;
  dashboardName: string;
  dashboardDescription: string;
  reportUrlSelect: SelectProps.Option;
  platformSelect: SelectProps.Option;
}

What is the right pattern to enforce the interface I defined above while allowing additional dynamic attributes that match a certain type?

In other words, how can I define an interface to require that all attributes in it are present but also allow zero or more additional attributes of a certain type BUT dynamic key name.

Additional values might follow the pattern [key: string]: string

An actual object would look like this:

{
  template: "test",
  dashboardName: "foo",
  dashboardDescription: "bar"
  reportUrlSelect: "test1"
  platformSelect: "hello world",
  dynamicallyGeneratedAttr: "I might not always be here",
  anotherDynamicAttr: "I'm also not guaranteed to be here all the time"
}

I know I can extend the interface and have also read about dynamic keys for interfaces but a dynamic key would not work in this case.

Thank you.

CodePudding user response:

Record

Record<Keys, Type> constructs an object type whose property keys are Keys and whose property values are Type. This utility can be used to map the properties of a type to another type.

Better explained in TS Docs.

Solution

You may want to use Record<string, string>

CodePudding user response:

You can merge in an index signature:

type Foo = ReportingNodeConfig & { [key: string]: string | undefined }

Testing it:

declare const foo: Foo

foo.template // string
foo.reportUrlSelect // SelectProps.Option

if (foo.someUnknownPropHere) {
    foo.someUnknownPropHere // string here
}

You'll definitely want it to resolve to a union with undefined, though, because you can't be sure those properties ever actually exist when you look them up.

Playground


That said, if you can change this data model, then you probably should. If you have free form property names, then they are very likely to be a totally different type of data and should be stored in a nested object instead.

Like say:

export interface ReportingNodeConfig {
  template: string;
  dashboardName: string;
  dashboardDescription: string;
  reportUrlSelect: SelectProps.Option;
  platformSelect: SelectProps.Option;
  customData: Record<string, string | undefined>
}

Now you can cram whatever you want in customData without polluting the statically verifiable property names.

  •  Tags:  
  • Related