Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Dynamically create type based on nested interface property

Given the following setup:

enum FormGroups {
  customer = 'customer',
  address = 'address',
}

interface Customer {
  'firstName': string;
}

interface Address {
  'street': string;
}

interface SomeFormEvent {
  valid: boolean;
  forms: {
    [FormGroups.customer]: {
      values: Customer,
    },
    [FormGroups.address]: {
      values: Address,
    }
  }
}

Now the following is what I would like to use:

type Forms = Customer | Address

However, this is static. If in the future a form is added in the SomeFormEvent like:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

interface SomeFormEvent {
  valid: boolean;
  forms: {
    [FormGroups.customer]: {
      values: Customer,
    },
    [FormGroups.address]: {
      values: Address,
    },
    // a form is added here:
    [FormGroups.preferences]: {
      values: Preferences,
    },
  }
}

I’d have to manually update the ‘Forms’ type:

// manually have to add 'Preferences'
type Forms = Customer | Address | Preferences

Is there a more dynamic way to create the Forms type?

I’ve tried the following:

enum FormGroups {
  customer = 'customer',
  address = 'address',
}

interface Customer {
  'firstName': string;
}

interface Address {
  'street': string;
}


interface SomeFormEvent {
  valid: boolean;
  forms: {
    [FormGroups.customer]: {
      values: Customer,
    },
    [FormGroups.address]: {
      values: Address,
    },
  }
}

type Forms = { [K in keyof SomeFormEvent['forms']]: SomeFormEvent['forms'][K]['values'] }

function foo(): Forms {
    return {
        // error: Type '{ firstName: string; street: string; }' is 
        //   not assignable to type 'Forms'. Object literal may only 
        //   specify known properties, and 'firstName' does not exist 
        //   in type 'Forms'.

        firstName: 'sdfdsf',
        street: 'sdffds'
    }
}

But that does not work.

>Solution :

Yes, we can create this union using indexed access types.

type Forms = SomeFormEvent["forms"][keyof SomeFormEvent["forms"]]["values"]
//   ^? Customer | Address

Playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading