diff --git a/src/dezerialize.ts b/src/dezerialize.ts index dd3b695..033423b 100644 --- a/src/dezerialize.ts +++ b/src/dezerialize.ts @@ -117,7 +117,7 @@ type DezerializersMap = { }; const dezerializers = { number: (shape) => { - let n = z.number(); + let n = shape.coerce ? z.coerce.number() : z.number(); if (shape.min !== undefined) { n = shape.minInclusive ? n.min(shape.min) : n.gt(shape.min); } @@ -136,7 +136,7 @@ const dezerializers = { return n; }, string: (shape) => { - let s = z.string(); + let s = shape.coerce ? z.coerce.string() : z.string(); if (shape.min !== undefined) { s = s.min(shape.min); } @@ -170,10 +170,10 @@ const dezerializers = { return s; }, - boolean: () => z.boolean(), + boolean: (shape) => shape.coerce ? z.coerce.boolean() : z.boolean(), nan: () => z.nan(), bigInt: (shape) => { - let i = z.bigint(); + let i = shape.coerce ? z.coerce.bigint() : z.bigint(); if (shape.min !== undefined) { i = shape.minInclusive ? i.min(shape.min) : i.gt(shape.min); } @@ -186,7 +186,7 @@ const dezerializers = { return i; }, date: (shape) => { - let i = z.date(); + let i = shape.coerce ? z.coerce.date() : z.date(); if (shape.min !== undefined) { i = i.min(new Date(shape.min)); } diff --git a/src/index.test.ts b/src/index.test.ts index 9197a27..5abaf07 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -404,3 +404,14 @@ test("discriminated union", () => { reach: 42, }); }); + +test("coerce", () => { + const schema = z.coerce.number(); + expect(schema.parse("42")).toEqual(42); + const shape = zerialize(schema); + expect(shape).toEqual({ + type: "number", + coerce: true, + }); + expect(dezerialize(shape as SzType).parse("42")).toEqual(42); +}); diff --git a/src/types.ts b/src/types.ts index a99a906..5b86662 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,7 @@ import { ValueOf } from "type-fest"; export type SzNumber = { type: "number"; + coerce?: boolean; min?: number; max?: number; minInclusive?: boolean; @@ -12,6 +13,7 @@ export type SzNumber = { }; export type SzBigInt = { type: "bigInt"; + coerce?: boolean; min?: bigint; max?: bigint; minInclusive?: boolean; @@ -31,6 +33,7 @@ export const STRING_KINDS = new Set([ export type SzString = { type: "string"; + coerce?: boolean; min?: number; max?: number; length?: number; @@ -59,11 +62,12 @@ export type SzString = { export type SzDate = { type: "date"; + coerce?: boolean; min?: number; max?: number; }; -export type SzBoolean = { type: "boolean" }; +export type SzBoolean = { type: "boolean"; coerce?: boolean }; export type SzNaN = { type: "nan" }; export type SzUndefined = { type: "undefined" }; export type SzNull = { type: "null" }; diff --git a/src/zerialize.ts b/src/zerialize.ts index 90fd5c1..10898b2 100644 --- a/src/zerialize.ts +++ b/src/zerialize.ts @@ -167,7 +167,10 @@ const zerializers = { }), {} ); - return { type: "number", ...checks }; + return Object.assign( + { type: "number", ...checks }, + def.coerce ? { coerce: true } : {} + ); }, ZodString: (def) => { const checks = def.checks.reduce( @@ -209,9 +212,13 @@ const zerializers = { }), {} ); - return { type: "string", ...checks }; + return Object.assign( + { type: "string", ...checks }, + def.coerce ? { coerce: true } : {} + ); }, - ZodBoolean: () => ({ type: "boolean" }), + ZodBoolean: (def) => + Object.assign({ type: "boolean" }, def.coerce ? { coerce: true } : {}), ZodNaN: () => ({ type: "nan" }), ZodBigInt: (def) => { const checks = def.checks.reduce( @@ -236,7 +243,10 @@ const zerializers = { }), {} ); - return { type: "bigInt", ...checks }; + return Object.assign( + { type: "bigInt", ...checks }, + def.coerce ? { coerce: true } : {} + ); }, ZodDate: (def) => { const checks = def.checks.reduce( @@ -253,7 +263,10 @@ const zerializers = { }), {} ); - return { type: "date", ...checks }; + return Object.assign( + { type: "date", ...checks }, + def.coerce ? { coerce: true } : {} + ); }, ZodUndefined: () => ({ type: "undefined" }), ZodNull: () => ({ type: "null" }),