TransWikia.com

Create a type of corresponding key/value pairs in TypeScript

Stack Overflow Asked by oyalhi on December 13, 2021

I would like to create a type so that it will consist of InputKeys as the keys, and the values will be key/value pairs, keys being the corresponding InputFieldKey, and value is string.

Below is what I have so far. But {[key: string]: string} the key in this context needs to be corresponding field of the main key.

Essentially there is the parent and child relationship constructed with <Record, the parent part is ok. But I can’t figure out the child part which needs to be parent / child again.

There could be as many or as little ???InputFieldKeys and those keys can have many fields not just 3 (firstName, lastName, ..., ...)

I am free to modify the code as necessary to create the needed structure, and/or use generics if that helps.

type InputKeys = 'user' | 'building'

type UserInputFieldKey = 'firstName' | 'lastName' | 'age'
type AddressInputFieldKey = 'line1' | 'line2' | 'zip'

type InputError = Record<InputKeys, {[key: string]: string}>

const errors: InputError = {
  user: {
    firstName: 'First name must be entered',
  },
  address: {},
}

Any help is appreciated.

One Answer

If you were doing this manually, it looks like this is the structure you're looking for:

interface InputErrorManual {
    user: {
        firstName?: string;
        lastName?: string;
        age?: string;
    },
    address: {
        line1?: string;
        line2?: string;
        age?: string;
    }
}

You could, of course, just do that.


If you want the compiler to do something for you programmatically, then you need to tell it about the parent/child relationship in some mapping type, like this:

type InputKeyMapping = {
    user: "firstName" | "lastName" | "age";
    address: "line1" | "line2" | "zip"
}

and then you can use a mapped type to convert that into the structure you're looking for:

type InputError = {
    [K in keyof InputKeyMapping]: Partial<Record<InputKeyMapping[K], string>>
};

You can verify that this is the same structure as in InputErrorManual.


And test them:

const goodErrors: InputError = {
    user: {
        firstName: 'First name must be entered',
    },
    address: {},
}; // okay

const badErrors: InputError = {
    user: {
        lastName: "bad name",
        line1: "oops" // error!
    },
    address: {
        line1: "bad line",
        age: "oops" // error!
    }
}

I'm not sure if doing the programmatic mapping is better or worse for your use case than writing it out manually. Either way should work for you.

Okay, hope that helps; good luck!

Playground link to code

Answered by jcalz on December 13, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP