Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BETA 3.20] Using transforms with discriminatedUnion #1667

Closed
brianalexander opened this issue Dec 12, 2022 · 1 comment
Closed

[BETA 3.20] Using transforms with discriminatedUnion #1667

brianalexander opened this issue Dec 12, 2022 · 1 comment

Comments

@brianalexander
Copy link

brianalexander commented Dec 12, 2022

Issue

I'd like to use Zod to label my parsed data and use it in a discriminatedUnion

Example:

import { z } from "zod";

const NotFoundSchema = z
  .undefined()
  .transform(() => ({ _tag: "not_found" as const }));
const SuccessSchema = z.object({ status: z.number().min(200).max(299) }).transform(x=> ({...x, _tag: "success" as const}));
const InternalErrorSchema = z.object({ status: z.number().min(500).max(599) }).transform(x=> ({...x, _tag: "internal_error" as const}));

// Type 'ZodEffects<ZodObject<{ status: ZodNumber; }, "strip", ZodTypeAny, { status: number; }, { status: number; }>, { _tag: "internal_error"; status: number; }, { status: number; }>' is missing the following properties from type 'ZodObject<{ _tag: ZodTypeAny; } & ZodRawShape, any, any, { [x: string]: any; }, { [x: string]: any; }>': _cached, _getCached, shape, strict, and 14 more.ts(2740)ZodTypeAny; } & ZodRawShape, any, any, { [x: string]: any; }, { [x: string]: any; }>': _cached, _getCached, shape, strict, and 14 more.ts(2740)
const ResponseStatusSchema = z.discriminatedUnion("_tag", [
  InternalErrorSchema,
  SuccessSchema,
  NotFoundSchema
]);

ResponseStatusSchema.parse({status:500})

https://codesandbox.io/s/confident-stallman-l1ej5p?file=/src/index.tsx

I thought 3.20 addressed this with #1290

If this is something Zod does not intend to cover, any recommendations on a working alternative? I currently use ts-pattern to pattern match out the correct item after using Zod to parse, but it's twice the work as Zod could give me the pattern match for free while parsing.

Thanks!

@brianalexander
Copy link
Author

This works for me in 3.20.2 using union and transform. Maybe it always worked or maybe it's new. Either way, thanks!!

Transforms happen after a successful match (makes sense), so as long as you use as const in transform they'll be available.

const NotFoundSchema = z
  .undefined()
  .transform(() => ({ _tag: "not_found" as const }));
const SuccessSchema = z
  .object({
    data: z.object({ a: z.number() }),
    status: z.number().min(200).max(299)
  })
  .transform((x) => ({ ...x, _tag: "success" as const }));
const InternalErrorSchema = z
  .object({ status: z.number().min(500).max(599) })
  .transform((x) => ({ ...x, _tag: "internal_error" as const }));

const ResponseStatusSchema = z.union([
  InternalErrorSchema,
  SuccessSchema,
  NotFoundSchema
]);

// { status: 500, data: {a: 1} }
// undefined
const res = ResponseStatusSchema.parse({ status: 500 });

switch (res._tag) {
  case "success":
    console.log(res.data);
    break;
  case "internal_error":
    console.log(res.status);
    break;
  case "not_found":
    console.log(res);
}

An alternative to this is coupling literal with default:

const SuccessSchema = z
  .object({
    _tag: z.literal("success").default("success"),
    data: z.object({ a: z.number() }),
    status: z.number().min(200).max(299)
  })

but this will only work with items that are already objects, while transform works with anything.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant