Skip to content

Commit

Permalink
Fixed freezing async ZodReadonly results (#3457)
Browse files Browse the repository at this point in the history
* docs: Corrected typo in readme

* test: result freezing after async parse

* fix: result freezing after async parse
  • Loading branch information
zsilbi authored May 2, 2024
1 parent 3fed6f2 commit 04e1f37
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2682,7 +2682,7 @@ Note that branded types do not affect the runtime result of `.parse`. It is a st
This method returns a `ZodReadonly` schema instance that parses the input using the base schema, then calls `Object.freeze()` on the result. The inferred type is also marked as `readonly`.

```ts
const schema = z.object({ name: string }).readonly();
const schema = z.object({ name: z.string() }).readonly();
type schema = z.infer<typeof schema>;
// Readonly<{name: string}>

Expand Down
50 changes: 50 additions & 0 deletions src/__tests__/readonly.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,53 @@ test("object freezing", () => {
)
).toBe(true);
});

test("async object freezing", async () => {
expect(
Object.isFrozen(await z.array(z.string()).readonly().parseAsync(["a"]))
).toBe(true);
expect(
Object.isFrozen(
await z.tuple([z.string(), z.number()]).readonly().parseAsync(["a", 1])
)
).toBe(true);
expect(
Object.isFrozen(
await z
.map(z.string(), z.date())
.readonly()
.parseAsync(new Map([["a", new Date()]]))
)
).toBe(true);
expect(
Object.isFrozen(
await z
.set(z.promise(z.string()))
.readonly()
.parseAsync(new Set([Promise.resolve("a")]))
)
).toBe(true);
expect(
Object.isFrozen(
await z.record(z.string()).readonly().parseAsync({ a: "b" })
)
).toBe(true);
expect(
Object.isFrozen(
await z.record(z.string(), z.number()).readonly().parseAsync({ a: 1 })
)
).toBe(true);
expect(
Object.isFrozen(
await z
.object({ a: z.string(), 1: z.number() })
.readonly()
.parseAsync({ a: "b", 1: 2 })
)
).toBe(true);
expect(
Object.isFrozen(
await z.promise(z.string()).readonly().parseAsync(Promise.resolve("a"))
)
).toBe(true);
});
13 changes: 9 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5041,10 +5041,15 @@ export class ZodReadonly<T extends ZodTypeAny> extends ZodType<
> {
_parse(input: ParseInput): ParseReturnType<this["_output"]> {
const result = this._def.innerType._parse(input);
if (isValid(result)) {
result.value = Object.freeze(result.value);
}
return result;
const freeze = (data: ParseReturnType<this["_output"]>) => {
if (isValid(data)) {
data.value = Object.freeze(data.value);
}
return data;
};
return isAsync(result)
? result.then((data) => freeze(data))
: freeze(result);
}

static create = <T extends ZodTypeAny>(
Expand Down

0 comments on commit 04e1f37

Please sign in to comment.