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

discriminatedUnion produces TS error when .default or .preprocess are applied #1490

Open
RobertCraigie opened this issue Oct 13, 2022 · 10 comments

Comments

@RobertCraigie
Copy link

Playground link.

Given this snippet:

import { z } from 'zod';

const FooSchema = z.object({
  type: z.literal('foo').default('foo'),
  a: z.string(),
});

const BarSchema = z.object({
  type: z.literal('custom'),
  method: z.string(),
});

const BazSchema = z.discriminatedUnion('type', [FooSchema, BarSchema]);
console.log(BazSchema.parse({ a: 'foo' }));

TypeScript produces this error:

$ tsc tmp.ts
tmp.ts:13:49 - error TS2322: Type 'ZodObject<{ type: ZodDefault<ZodLiteral<"foo">>; a: ZodString; }, "strip", ZodTypeAny, { type?: "foo"; a?: string; }, { type?: "foo"; a?: string; }>' is not assignable to type 'ZodDiscriminatedUnionOption<"type", Primitive>'.
  Type '{ type: z.ZodDefault<z.ZodLiteral<"foo">>; a: z.ZodString; }' is not assignable to type '{ type: ZodLiteral<Primitive>; } & ZodRawShape'.
    Type '{ type: z.ZodDefault<z.ZodLiteral<"foo">>; a: z.ZodString; }' is not assignable to type '{ type: ZodLiteral<Primitive>; }'.
      Types of property 'type' are incompatible.
        Property 'value' is missing in type 'ZodDefault<ZodLiteral<"foo">>' but required in type 'ZodLiteral<Primitive>'.

13 const BazSchema = z.discriminatedUnion('type', [FooSchema, BarSchema]);
                                                   ~~~~~~~~~

  node_modules/zod/lib/types.d.ts:531:9
    531     get value(): T;
                ~~~~~
    'value' is declared here.


Found 1 error in tmp.ts:13

This code works at runtime however and Zod correctly parses the object:

{ type: 'foo', a: 'foo' }

Previously opened as #1263 but was closed as stale.

okjulian added a commit to okjulian/zod that referenced this issue Oct 14, 2022
This fixes type checking when passing literal types with default values or preprocess transformations.

Before this change, the type checker was complaining that value does not exist, as described in colinhacks#1490.

The fix consists in modifying the `ZodDiscriminatedUnionOption` to also allow `ZodDefault<ZodLiteral...>` and `ZodEffects<ZodLiteral...>`.

Added a test case that had failing types but now works.

Note: An even better solution I imagine is making sure that both `ZodDefault` and `ZodEffects` re-surface the inner type properties, such as `ZodLiteral`'s `value`. But I'd need more familiarity with the code base to propose that change.
@luixo
Copy link

luixo commented Oct 17, 2022

Seems like there is a same problem if you refine one of the object schemas inside a union as well:

z.discriminatedUnion('type', [
  z.strictObject({
    type: z.literal('foo'),
  }),
  z.strictObject({
    type: z.literal('bar'),
  }).refine((input) => true, 'Error'),
]);

This one produces an error:

Type 'ZodEffects<ZodObject<{ type: ZodLiteral<"bar">; }, "strict", ZodTypeAny, { type: "bar"; }, { type: "bar"; }>, { type: "bar"; }, { type: "bar"; }>' is missing the following properties from type 'ZodObject<{ type: ZodLiteral<Primitive>; } & ZodRawShape, any, any, { [x: string]: any; }, { [x: string]: any; }>': _cached, _getCached, shape, strict, and 14 more.
'input' is declared but its value is never read.

And here's an ugly workaround for the refinement problem:

const bar = 
  z.strictObject({
    type: z.literal('bar'),
  }).refine((input) => true, 'Error');

type ExtractRefinementType<T extends z.ZodTypeAny> = T extends z.ZodEffects<
  infer T,
  any,
  any
>
  ? ExtractRefinementType<T>
  : T;

z.discriminatedUnion('type', [
  z.strictObject({
    type: z.literal('foo'),
  }),
  bar as unknown as ExtractRefinementType<typeof bar>
]);

@stale
Copy link

stale bot commented Jan 15, 2023

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.

@stale stale bot added the stale No activity in last 60 days label Jan 15, 2023
@RobertCraigie
Copy link
Author

This would still be very nice to have!

@stale stale bot removed the stale No activity in last 60 days label Jan 15, 2023
@stale
Copy link

stale bot commented Apr 17, 2023

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.

@stale stale bot added the stale No activity in last 60 days label Apr 17, 2023
@luixo
Copy link

luixo commented Apr 17, 2023

@colinhacks I believe this may be marked as good first issue.

@stale stale bot removed the stale No activity in last 60 days label Apr 17, 2023
@doteric
Copy link

doteric commented May 19, 2023

Hey @RobertCraigie 👋
When I try the exact same approach you've mentioned I receive a runtime zod error of:

ZodError: [
      {
        "code": "invalid_union_discriminator",
        "options": [
          "foo",
          "custom"
        ],
        "path": [
          "type"
        ],
        "message": "Invalid discriminator value. Expected 'foo' | 'custom'"
      }
    ]

I was trying to default to a specific zod object if the type (in your example) is undefined, but it doesn't seem to be straightforward. Sadly z.discriminatedUnion doesn't seem to work, but the normal z.union seems to work fine.

@stale
Copy link

stale bot commented Aug 17, 2023

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.

@stale stale bot added the stale No activity in last 60 days label Aug 17, 2023
@luixo
Copy link

luixo commented Aug 17, 2023

Not stale

@stale stale bot removed the stale No activity in last 60 days label Aug 17, 2023
@grunklejp
Copy link

Still seeing this error when trying to apply a .default to discriminatedUnion as of v3.22.4.

@flodlc
Copy link

flodlc commented Aug 17, 2024

Still happening with refine too

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

5 participants