0

I have some data I create manually:

const fields = [{ id: 'myId', value: '', type: 'text' }, { id: 'myId2', value: '', type: 'select' }]

OR

const fields = { myId: { value: '', type: 'text' }, myId2: { value: '', type: 'select' } }

I would like to build an interface like:

type Out = { myId: string; myId2: CustomType }

string or CustomType will depends on what I pass to type field.

Entire example will be used to build type for values in onSubmit handler which will define available fields (by id from fields data) and depends what it defined in type field in input data (union type).

Bruce Lee
  • 77
  • 9
  • Just add `as const` at the end of the array literal, then use `type Out = typeof fields[number];` to get `Out`. This is covered by many previous questions and their answers, I'll try to find a couple to point to. For instance, [here's one](https://stackoverflow.com/questions/75820967/extract-props-from-objects-in-array-as-union-without-making-the-array-readonly), but it has an additional constraint you don't seem to have. – T.J. Crowder Mar 23 '23 at 17:43
  • At least related: https://stackoverflow.com/questions/44497388/typescript-array-to-string-literal-type, https://stackoverflow.com/questions/72281294/define-an-interface-for-object-as-const – T.J. Crowder Mar 23 '23 at 17:45
  • it will not work since it gives me `{id: "myId", type: "text", value: ""} | {id: "myId2", type: "select", value: ""}` as `Out` type – Bruce Lee Mar 23 '23 at 17:49
  • That just requires tweaking with a mapped type. What are you expecting `CustomType` to be? And which of your examples are you trying to do this for? Right now you're asking two different things. – T.J. Crowder Mar 23 '23 at 18:43

1 Answers1

1

You'll first need a map of type names (strings) to TypeScript types:

interface TypeMap {
    text: string;
    select: CustomType;
}

Then, you can define a type that can take an array of fields and create a type from them, using mapped types:

type TypeFromArray<A extends readonly { id: string; value: string; type: keyof TypeMap }[]> = {
    [K in A[number] as K["id"]]: TypeMap[K["type"]];
};

const fields = [
    { id: "myId", value: "", type: "text" },
    { id: "myId2", value: "", type: "select" },
] as const; // note the 'as const'

type X = TypeFromArray<typeof fields>;
//   ^? { myId: string; myId2: CustomType }

Similarly, you can also create a type to transform an object:

type TypeFromObject<O extends Readonly<Record<string, { value: string; type: keyof TypeMap }>>> = {
    -readonly [K in keyof O]: TypeMap[O[K]["type"]];
};

const fields = {
    myId: { value: "", type: "text" },
    myId2: { value: "", type: "select" },
} as const; // note the 'as const'

type X = TypeFromObject<typeof fields>;
//   ^? { myId: string; myId2: CustomType }

Playground

kelsny
  • 23,009
  • 3
  • 19
  • 48