profile
viewpoint

Ask questionsValue transformation / object mapping

Hey! I just tried zod and I quite enjoy it so far, great job! I have a feature request/discussion.

Problem: I would like my schema to not only validate data but transform it as well. A particular use case is that I would like to specify a mapping in my object schema.

One could imagine a type z.stringAsNumber() which would parse strings like "123" to a number 123. Something like this is totally possible with io-ts.

Or even more powerful case. Where object validation could remap keys:

const envSchema = z.object({
  SERVER_HOST: z.string(),
  PUBLIC_S3_URL: z.string(),
  PRIVATE_BACKEND_URL: z.string(),
})

This schema is used to validate environment variables. It would be cool if I could specify a key mapping to end up with renamed keys in a parsed object for example (example API)

const envSchema = z.object({
  serverHost: { value: z.string(), key: 'SERVER_HOST' },
  publicS3: { value: z.string(), key: 'PUBLIC_S3_URL' }
  privateBackendURL: { z.string(), key: 'PRIVATE_BACKEND_URL' }
})

I feel like this is a very common situation. Of course, it can be done manually after schema validation but I believe that it would be cool to support this one way or another. WDYT @vriad? Is this something that you want to support eventually?

vriad/zod

Answer questions chrbala

Seems like this could be really useful when it comes to more complex objects. For a specific use-case I'm thinking of, take GraphQL unions that are generated from inputs. Union types only exist on outputs, so it's required to use some workarounds to have varied inputs.

The following is a common solution for this problem illustrated in TypeScript:

type Animal = {
  lifespan: number;
};

type Mineral = {
  elements: Array<string>;
};

type Vegetable = {
  calories: number;
};

type Answer = Animal | Mineral | Vegetable;

type TwentyQuestions = {
  answer: Answer;
};

// This is equivalent to TwentyQuestions shown above, but structured differently.
// It is also possible to represent invalid values with this structure.
type TwentyQuestionsInput = {
  animal?: Animal;
  mineral?: Mineral;
  vegetable?: Vegetable;
};

The pattern here is to make each variant of the union a unique field on the input type which is then validated in the API logic to ensure that only one is provided and persisted.

Concretely for zod, this would look like a union on one side and an object on the other side with some additional logic (with zod refine) to ensure that only one key is provided.

There are some patterns in GraphQL and in other systems that require transformations like this. Creating complex structures for both validation/parsing and mapping would be fairly redundant, so it would be ideal to include this type of logic in a library like zod.

A couple related thoughts:

  • What actually gets validated? If the parser maps A -> B, does the validation logic operate on A or B?
  • What do you do if you want to have mappings from both A -> B as well as A -> C? There’s likely a lot of common logic and structure there. What is the best way of sharing it?
  • Should there be library logic for composing transformations? That is, if you have logic for A -> B and B -> C, should there be something like a z.compose(a, b) that creates an A -> C mapping from the separate mappers? How would that work?
  • What about reversible operations? How would you easily create both A -> B and B -> A?
useful!

Related questions

No questions were found.
source:https://uonfu.com/
Github User Rank List