Search code examples
genericstypescript-typingstypescript-genericsreact-typescript

TypeScript: typing the object key from an object that is inside another Object


First of all, sorry for the bad title. I have a problem where I want to type the keys of a field inside a nested object.

Here is a snippet of my types:

type FormConfig<FormFieldIds extends string> = Record<FormFieldIds, OptionsConfig<FormFieldIds>>;

interface OptionsConfig<FormFieldOptions extends string> {
  type: 'checkbox';
  label: string;
  options: Record<FormFieldOptions, ValuePair>;
}

interface ValuePair {
  value: string;
  label: string;
}

My hook:

function useMyForm<FormFieldIds extends string>(
  config: FormConfig<FormFieldIds>
): {
  form: FormConfig<FormFieldIds>;
} {
  return { form: config };
}

My component:

export const MyPage = () => {
  const { form } = useMyForm({
    weekdays: {
      type: 'checkbox',
      label: 'Pick some days',
      options: { monday: { label: 'Monday is a great day!', value: 'monday' } },
    },
    months: {
      type: 'checkbox',
      label: 'Pick som months',
      options: { january: { label: 'January is cold', value: 'january' } },
    },
  });
};

This works perfectly when I only have one object, but when I add more objects TypeScript complains.

So the problem is that I cant type the keys of options object inside each object of the configuration. Does anybody know how I can do that?

I have made a sandbox with the code that gives more explanation. https://codesandbox.io/s/stupefied-leaf-ss2fi2?file=/src/App.tsx


Solution

  • You should just use a Record:

    interface ValuePair {
      value: string;
      label: string;
    }
    
    interface OptionsConfig<FormFieldOptions extends string> {
      type: "checkbox";
      label: string;
      options: Record<FormFieldOptions, ValuePair>;
    }
    
    function useMyForm<
      Conf extends Record<string, OptionsConfig<string>>
    >(
      config: Conf
    ): {
      form: Conf;
    } {
      return { form: config };
    }
    

    Playground