Generic type for an array of objects, with custom object property names

Issue

I have a data property inside of an object I’m looking to type in Typescript. It looks like the following:

type MyThing = {
  data: {
    options: {
      myKey: string,
      myValue: string
    }[],
    key: 'myKey',
    value: 'myValue'
  }
}

I would like to be able to instantiate types of this structure using a generic but can’t quite wrap my mind around what I should be doing. Here are the requirements:

  1. key and value are the base "keys" that must be supplied, but there may be others: description for instance
  2. options should always be an array of objects containing only the values of the key, value, and possibly description defined below

I have no problems typing this literally (like in the above example). But if I want to supply custom keys and values ("myKey" and "myValue" respectively), or if I want to specify other options like a description beyond the key and value, I cannot figure out how to do such.

The desired solution would be for me to do something like this:

type MyThing = {
  data: DataComposer<['key', 'value']>
  // data: DataComposer<['key', 'value', 'description', ...]>
}

Solution

I think you want a type like this:

type DataComposer<
    K extends string,
    V extends string,
    OtherKeys extends Record<string, string> = Record<never, string>
> = {
    data: {
        options: { [optionKey in K | V | OtherKeys[keyof OtherKeys]]: string}[],
        key: K,
        value: V,
    } & { [OtherKey in keyof OtherKeys]: OtherKeys[OtherKey] }
}

Here K is the string literal for the key and V is for the value. Then it accepts a third parameter for a mapping of other custom properties.

Which you use like this:


type MyThing = DataComposer<'myKey', 'myValue'>
/*
type MyThing = {
    data: {
        options: {
            myKey: string;
            myValue: string;
        }[];
        key: "myKey";
        value: "myValue";
    };
}
*/

type MyThingWithOther = DataComposer<'myKey', 'myValue', { foo: 'myFoo' }>
/*
type MyThingWithOther = {
    data: {
        options: {
            myKey: string;
            myValue: string;
            myFoo: string;
        }[];
        key: "myKey";
        value: "myValue";
        foo: 'myFoo';
    };
}
*/

Answered By – Alex Wayne

Answer Checked By – Marilyn (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.