Array element child type

Viewed 418

A question regarding type construction from data structures. I have an array like this:

interface ICVSection {
  namespace: string;
  validation: Yup.ObjectSchema<any>;
}

const CVSections: ICVSection[] = [
  {
    namespace: 'common',
    validation: CommonValidationSchema,
  },
  {
    namespace: 'summary',
    validation: SummarySchema,
  }
  /* ... */
};

So the question is: is it possible to somehow using the data above create following type:

type Schema = {
  common: typeof CommonValidationSchema,
  namespace: typeof SummarySchema,
  /* ... */
}

I'm not 100% sure if it is possible in TypeScript, but if so, it would be great if someone can prove me right or wrong.

1 Answers

So here is the solution I've come up with:

type namespaces = typeof CVSections[number]['namespace'];

type Schema = {
  [K in namespaces]: Pick<Extract<typeof CVSections[number], { namespace: K }>, 'validation'>['validation']>;
};

But to make it work we need to alter our array definition a bit. Instead of typing it as ICVSection[], we have to define it as const CVSections = [ /* ... */ ] as const; to allow TypeScript narrow down possible namespaces values.

In my case, as I'm using Yup library with Formik for validation schema, I can apply additional type conversion to unwrap Yup's Schema type to plain TypeScript type. Final solution will look like this:

type Schema = {
  [K in namespaces]: Yup.InferType<Pick<Extract<typeof CVSections[number], { namespace: K }>, 'validation'>['validation']>;
};

Then we can apply this type to <Formik> component to enforce type validation:

<Formik<Schema>
  initialValues={{
    common: {
      /* mandatory values */
    },
    summary: {
      /* mandatory values */
    }
  }}
>
Related