-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Discriminated unions became incompatible with zod.input
#1196
Comments
The error seems to be related to the changes in Typescript starting from v4.6. I submitted an issue to track it. |
The TS guys claim that this is a correct behaviour from their side, so that |
Yeah, I agree with them that this is correct behavior and I'm not familiar with any way at all we could widen the type that is created to be explicitly narrowed like this. If you want your type to be |
We use the approach of matching the type inferred from the schema with the real type that is passed to the validator. Having it this way ensures that the schema and the model are in sync, so for example the following code yields an error: class ModelEditor<TModel extends input<S>, S extends ZodType<unknown>> {
constructor(public model: TModel, public schema: S) {}
}
const schema = zod.object({
foo: zod.number()
})
class Model {
foo: number | undefined;
}
export class AdvancedEditor extends ModelEditor<Model, typeof schema> {}
/*
Type 'Model' does not satisfy the constraint '{ foo: number; }'.
Types of property 'foo' are incompatible.
Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.ts(2344)
*/ You see, this provides consistency for the schema-model pair. const schema = zod.union([
zod.object({
flag: zod.literal(true),
foo: zod.string().min(1),
}),
zod.object({
flag: zod.literal(false),
foo: zod.string().optional(),
})
]);
schema.parse({ flag: false }); // succeeds
schema.parse({ flag: true }); // fails
schema.parse({ flag: true, foo: '' }); // fails
schema.parse({ flag: true, foo: 'bar' }); // succeeds So now (TS 4.6+) it's no longer possible to define an ordinary Model class that corresponds to a discriminated union provided by zod schema. |
Isn't the model class: interface FlagTrue {
flag: true;
foo: string;
}
interface FlagFalse {
flag: false;
foo?: string;
}
type Flag = FlagTrue | FlagFalse —? That is precisely the type-level type that the runtime schema is representing. If your type is: interface Flag {
flag: boolean;
foo?: string;
} I think you'll want to make your schema reflect that rather than trying to make a union do something clever here that just happened to work before due to TypeScript not having good control-flow semantics with discriminated unions. In other words: Your model (if it must be defined outside of Zod) should actually match the Zod schema. If it doesn't, you'll have problems like this and others as the type system and runtime system try to keep matching each other. |
I get your point now. |
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. |
The TS team confirmed a bug on their side, which was fixed in v4.8 |
The issue is reproducible with Typescript v. 4.6+ and is very likely caused by its new feature called Control Flow Analysis for Destructured Discriminated Unions.
Here is a sandbox that reproduces the error. Run
yarn build
in the terminal tab.Essentially, here's the compiler error that I get when trying to reference the result of
zod.input
that was applied to a discriminated union type constructed usingzod.union
(classic way):And nothing changes if I use the brand new
zod.discriminatedUnion
:The issue is not reproducible in Typescript v. 4.5 and older.
The text was updated successfully, but these errors were encountered: