ianstormtaylor / superstruct

A simple and composable way to validate data in JavaScript (and TypeScript).
https://docs.superstructjs.org
MIT License
6.95k stars 224 forks source link

Intersection of type schema and record with enums as keys #1235

Open alexamy opened 3 months ago

alexamy commented 3 months ago

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 */
    });
  2. 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 commented 3 months ago

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

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