question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Derivative schema usage, undocumented methods, and generic functions

See original GitHub issue

Hey! in the last few days I became obsessed with zod after years of being driven crazy with working with “soft” interfaces from trash api. nevermind that, I have a proposal(?) and a question.

There are a few situations that I encountered with zod which I hit a dead end. The first is a generic wrapper. Lets say the data I’m working on has this repeated pattern:

{
  "data": {
    "id":  "1",
    "attributes": {
      "name": "zod",
      "author": {
        "id": "2",
        "attributes" : {
          "name": "colinhacks"
        }
      }
    }
  }
}

or, in other words

interface Payload<T> {
  data: {
    id: string
    attributes: T
  }
}

I’d wish to make generic helper or generic schema for these situation. With an important distinction. I want the schema to transform (read strip) these boilerplate fields. But TS breaks and won’t let me 🤷🏻‍♂️

// this doesn't work
export const gqlPayload = <T extends object>(shape: T) =>
  z.object({
    data: z.object({
      id: z.string(),
      attributes: z.object(shape),
    }),
  }).transform(({data}) => ({id: data.id, ...data.attributes}))

// nor does this
export const gqlPayload = <T extends z.ZodTypeAny>(schema: T) =>
  z.object({
    data: z.object({
      id: z.string(),
      attributes: schema,
    }),
  }).transform(({data}) => ({id: data.id, ...data.attributes}))

very sad, next issue


I also hit a wall when I wanted to continue working from an already-parsed object. for example lets say I have a Dragon schema, and the schema makes a few transformations, and now I have a startQuery function, that should recieve that transformed Dragon, and extend/transform it again. but I could only set the input as the untransformed Dragon. Which sucks eggs.

example:

const Wings = z.object({
  leftHp: z.number(),
  rightHp: z.number()
}).transform(({leftHp, rightHp}) => [leftHp, rightHp])

const Dragon = z.object({
  wings: Wings,
  legs: z.number(),
  eyes: z.enum(['red', 'blue'])
})


// requires a Dragon with array wings instead of object wings
const SlainDragon = Dragon.outputSchema.refine(/** check for less than 5 legs or something */).transform(value => ({...value, wings: [0,0]}))
type DragonToSlay = z.input<typeof SlainDragon>
// {
//   wings: [number, number];
//   legs: number;
//   eyes: "red" | "blue";
// }

function slayDragon(dragon:  Dragon) {
  try {
    const slainDragon = SlainDragon.parse(dragon)
    return slainDragon
  } catch (e) {
    throw new Error('you cannot hope to slay a beast with that many legs!')
  }
}

I know the example is a bit out there. The real-world example is just a bit more complicated. But basically it’s getting an Item from somewhere, transforming it to be usable in my app, and in some other interaction take that transformed item and transform it more before sending it back. I could use 2 schemas, but then I’ll have to make sure they are synced all the time. I just want the output of one schema to be the input of a different schema… 😢


last but not least, whats up with all the undocumented stuff? shape, innerShape(), describe, tell me what they doooooooo why do I have to figure it out myselfffffff (no need for all the _ prefixed ones, you can have your secrets 😉 )

I love zod, and sorry for the long issue. I’d open a PR instead, but I know nothing about the inner workings of zod, so I thought it’d approach you first

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
scotttrinhcommented, Apr 15, 2022

@datner

The dragon problem is not solved by that. I meant incremental changes. Like having a schema with a transform, parsing unknown to shape A and transforming it to Shape B. Then, continue from shape B, and make something on it. Like making sure that a different unknown is of shape B and transforming it or refining it.

I’m not sure I follow the issue here. You can declare as many chained transforms as you want and the z.output mapped type will correctly give you the output type. If you need some intermediate type, you’ll have to break apart the schema since there isn’t a straightforward way to query like z.thirdTransform<typeof Dragon>.

The use-case is for a form, where I get awkward data from the server, I want to pass it through zod and transform it into a shape that I use in the form. Then, after the form is done with, add constraints to the new shape, before transforming it again to my dto and send it away.

Can you give us a more straightforward example of your use case here? Going off of your description sounds something like this:

CodeSandbox

const ServerModel = z.object({
  name: z.string(),
  age: z
    .string()
    .refine((str) => Number.isNaN(Number(str)))
    .transform(Number)
});
type ServerModelInput = z.input<typeof ServerModel>; // { name: string; age: string }
type ServerModelOutput = z.output<typeof ServerModel>; // { name: string; age: number }

const DtoModel = ServerModel.extend({
  age: z.number().transform(String)
});
type DtoModelInput = z.input<typeof DtoModel>; // { name: string; age: number }
type DtoModelOutput = z.output<typeof DtoModel>; // { name: string; age: string }
0reactions
stale[bot]commented, Jun 22, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Generic derivation: the hard parts - Travis Brown
... relying on undocumented behavior * Having types supports some advanced features ... faster approach (for now): * Use a macro materializer for...
Read more >
uiinspect - Undocumented Matlab
uiinspect is a Matlab utility that displays detailed information about an object's methods, properties and callbacks in a single GUI window.
Read more >
Computational Methods in Drug Discovery - PMC - NCBI
Kim et al. (2008) present a method for defining the topology of the protein as a Voronoi diagram of spheres and its use...
Read more >
Documenting legal status: a systematic review of ...
We discuss the definition of undocumented status, conduct a systematic review of the methodological approaches currently taken to measure ...
Read more >
Writing R Extensions - The Comprehensive R Archive Network
7 Generic functions and methods. 7.1 Adding new generics. 8 Linking GUIs and other front-ends to R. 8.1 Embedding R under Unix-alikes.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found