Home > other >  How to declare type/interface with dynamic props
How to declare type/interface with dynamic props

Time:10-07

I'm trying to type an interface (or type) with the following logic:

interface MyArgs<Name, Id> {
  items: {
    [Name]: string,
    [Id]: string
  }[];
  idKey: string;
  nameKey: string;
}

In more detail:

I'm getting an array of items of an unknown type/interface (it's dynamic, they can be anything). While I could just type them as any[], I'd like to know if it's possible to be a bit more specific about their type, and pass Typescript the information that I do have:

each item will be having an id property (e.g: guid, id, uuid) and a name property (e.g: name, label, title). The exact idKey and nameKey are provided as arguments (idKey, nameKey).

I want to declare the values provided in idKey and nameKey as valid keys for items records (e.g: { [idKey]: string; [nameKey]: string }(

What I tried above doesn't work. I also tried this:

interface MyArgs<Name, Id> {
  items: { <Name>: string; <Id> : string }[]
  idKey: string
  nameKey: string
}

which is, too, invalid.

How can I achieve this?

Note: if the above would be possible without having to type the values twice (once inside <> and once in the values of idKey and nameKey, that would be even more awesome).

CodePudding user response:

Intersect mapped types (or rather, Records) to create the correct object type:

interface MyArgs<Name extends string, Id extends string> {
  items: (
    { [_ in Name]: string; } &
    { [_ in Id]: string; }
  )[];
  idKey: string;
  nameKey: string;
}

If you need to, you can also constrain Name and Id to more specific keys:

interface MyArgs<Name extends "name" | "label" | "title", Id extends "guid" | "id" | "uuid"> {

You could also use Records to simplify it further:

  items: (
    Record<Name, string> & Record<Id, string>
  )[];

Or even just:

  items: Record<Id | Name, string>[];
  • Related