Skip to content

Commit 72a05c4

Browse files
authored
feat(v4): expose stringbool truthy/falsy/case via _zod.bag (#6357)
`z.stringbool()` builds a `$ZodCodec` whose transform closes over the resolved `truthy`/`falsy` values, so nothing could read them back off the schema. Introspection tools were regexing `transform.toString()` just to detect a stringbool, and the values stayed out of reach either way. The resolved config now lands on `._zod.bag` after construction, the same way `z.instanceof` exposes `bag.Class`. The stored arrays are the normalized ones — lowercased unless `case: "sensitive"` — so they match what the codec actually accepts, and `truthy[0]`/`falsy[0]` are what it encodes back to. Requested in #5011.
1 parent 036b39f commit 72a05c4

3 files changed

Lines changed: 25 additions & 0 deletions

File tree

packages/zod/src/v4/classic/tests/stringbool.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,21 @@ test("z.stringbool codec round trip", () => {
104104
const encodedFalse = z.encode(schema, decodedFalse);
105105
expect(encodedFalse).toEqual("disabled"); // First element of falsy array
106106
});
107+
108+
test("z.stringbool exposes truthy/falsy/case via _zod.bag", () => {
109+
const a = z.stringbool();
110+
expect(a._zod.bag.truthy).toEqual(["true", "1", "yes", "on", "y", "enabled"]);
111+
expect(a._zod.bag.falsy).toEqual(["false", "0", "no", "off", "n", "disabled"]);
112+
expect(a._zod.bag.case).toEqual("insensitive");
113+
114+
// values are normalized when case-insensitive
115+
const b = z.stringbool({ truthy: ["Y", "yes"], falsy: ["N"] });
116+
expect(b._zod.bag.truthy).toEqual(["y", "yes"]);
117+
expect(b._zod.bag.falsy).toEqual(["n"]);
118+
expect(b._zod.bag.case).toEqual("insensitive");
119+
120+
const c = z.stringbool({ truthy: ["Y"], falsy: ["N"], case: "sensitive" });
121+
expect(c._zod.bag.truthy).toEqual(["Y"]);
122+
expect(c._zod.bag.falsy).toEqual(["N"]);
123+
expect(c._zod.bag.case).toEqual("sensitive");
124+
});

packages/zod/src/v4/core/api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,6 +1828,10 @@ export function _stringbool(
18281828
error: params.error,
18291829
}) as any;
18301830

1831+
codec._zod.bag.truthy = truthyArray;
1832+
codec._zod.bag.falsy = falsyArray;
1833+
codec._zod.bag.case = params.case ?? "insensitive";
1834+
18311835
return codec;
18321836
}
18331837

packages/zod/src/v4/mini/tests/index.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,12 +912,15 @@ test("z.stringbool", () => {
912912
expect(z.parse(b, "n")).toEqual(false);
913913
expect(z.safeParse(b, "true")).toMatchObject({ success: false });
914914
expect(z.safeParse(b, "false")).toMatchObject({ success: false });
915+
expect(b._zod.bag.truthy).toEqual(["y"]);
916+
expect(b._zod.bag.falsy).toEqual(["n"]);
915917

916918
const c = z.stringbool({
917919
case: "sensitive",
918920
});
919921
expect(z.parse(c, "true")).toEqual(true);
920922
expect(z.safeParse(c, "TRUE")).toMatchObject({ success: false });
923+
expect(c._zod.bag.case).toEqual("sensitive");
921924
});
922925

923926
// promise

0 commit comments

Comments
 (0)