dndr
dndr

Reputation: 2359

Infer property names from array property values in Typescript

Given the interface

interface FormField {
    name: string;
}

and a value such as:

const fields: FormField[] = [
    { name: 'givenName' },
    { name: 'familyName' }
]

I would like to infer this interface:

interface FormModel {
    givenName: string;
    familyName: string;
}

So use the array's property values as property names.

I tried something like:

type Model<T extends Array<FormField>> = { [K['name'] in T]: string };

but this doesn't work. Is this kind of thing possible in Typescript?

Upvotes: 0

Views: 907

Answers (1)

ford04
ford04

Reputation: 74820

Your solution already goes in the right direction, we can tune the types a bit up to make it work:

// make name generic, so we can refer to concrete string literal
interface FormField<T extends string> {
    name: T;
}

const fields = [
    { name: 'givenName' },
    { name: 'familyName' }
] as const  // const assertion to retain strings "givenName" and "familyName"

// T[number] gives all possible array values, T[number]["name"] all possible names
type Model<T extends ReadonlyArray<FormField<any>>> = { [K in T[number]["name"]]: string };

type FormModel = Model<typeof fields> // { givenName: string; familyName: string; }

fields doesn't have an explicit type FormField[] anymore in order to preserve literal types. If you still want to have stong type checks for fields, you can use a factory function:

const formField = <T extends string>(t: T): FormField<T> => ({ name: t })

const fields = [
  formField('givenName'),
  formField('familyName'),
  formField(3) // 💣, error
]

Code sample

Upvotes: 1

Related Questions