superstruct icon indicating copy to clipboard operation
superstruct copied to clipboard

Intersection of type schema and record with enums as keys

Open alexamy opened this issue 1 year ago • 1 comments

How to create schema for this object?

{
  base: { version: "v1" }, // may be "v2", etc
  en: { /* lang constant */ },
  ge: { /* lang constant */ },
  fr: { /* lang constant */ },
  /* ... long list of languages ... */
}

I've found https://github.com/ianstormtaylor/superstruct/issues/1162#issuecomment-1860205487, https://github.com/ianstormtaylor/superstruct/issues/480#issuecomment-733274941:

intersection([
  type({ version: enum(["v1", "v2"]) }),
  record(string() /* <-- should be enums(["en", ... ]) */, LangConstant)
])

But the problem is, as stated in the comment, that I lose list of remaining keys of the object. And even if I use enums() instead of string() as key, the schema wont validate properly (because of missing base key or wrong value type below base). So there are options:

  1. Add all languages in a object schema:
s.object({
  base: object({ version: enum(["v1", "v2"]) }),
  en: LangConstant,
  ge: LangConstant,
  /* ...other languages */
});
  1. Add helpers:
const languages = ["en", "ge", /* ...other languages */];
const langsSchema = langs.reduce((acc, lang) => {
  return { ...acc, [lang]: LangConstant };
}, {});

s.object({
  base: object({ version: enum(["v1", "v2"]) }),
  ...langsSchema,
});

But the first one have a lot of repetition, and second have a separation from superstruct.

Is there a more concise way to specify such schema?

alexamy avatar May 22 '24 09:05 alexamy

For now I'm using a condensed option similar to 2:

const langsSchema = Object.fromEntries([
  "en", "ge", "fr",
  /* ... */
].map(lang => ([lang, s.object({
    /* ... */
  })
])));

alexamy avatar May 22 '24 09:05 alexamy

I just close it with this as the solution.

alexamy avatar Feb 25 '25 19:02 alexamy

I added a comment to another issue with a slightly different approach using a custom superstruct utility function mappedObject().

Only downside is that one needs to define every single key explicitly.

mellanslag-de avatar Jun 05 '25 09:06 mellanslag-de