Skip to content

Commit dc7bd91

Browse files
authored
Reusing z.object({}) for describing EmptySchema and EmptyObject (#2740)
Fixed in zod 3.25.61: colinhacks/zod@1c2ad87#comments Elevating peer dependency requirement is breaking, therefore addressing to v25
1 parent f89e8a7 commit dc7bd91

7 files changed

Lines changed: 46 additions & 9 deletions

File tree

express-zod-api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"express-fileupload": "^1.5.0",
7676
"http-errors": "^2.0.0",
7777
"typescript": "^5.1.3",
78-
"zod": "^3.25.35"
78+
"zod": "^3.25.61"
7979
},
8080
"peerDependenciesMeta": {
8181
"@types/compression": {

express-zod-api/src/common-helpers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import { CommonConfig, InputSource, InputSources } from "./config-type";
66
import { contentTypes } from "./content-type";
77
import { AuxMethod, Method } from "./method";
88

9+
/** @since zod 3.25.61 output type fixed */
10+
export const emptySchema = z.object({});
11+
export type EmptySchema = typeof emptySchema;
912
/** @desc this type does not allow props assignment, but it works for reading them when merged with another interface */
1013
export type EmptyObject = z.output<EmptySchema>;
11-
/** Avoiding z.ZodObject<Record<string, never>, $strip>, because its z.output<> is generic "object" (external issue) */
12-
export type EmptySchema = z.ZodRecord<z.ZodString, z.ZodNever>;
1314
export type FlatObject = Record<string, unknown>;
1415

1516
/** @link https://stackoverflow.com/a/65492934 */

express-zod-api/src/endpoints-factory.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { Request, Response } from "express";
22
import { z } from "zod/v4";
3-
import { EmptyObject, EmptySchema, FlatObject, Tag } from "./common-helpers";
3+
import {
4+
EmptyObject,
5+
emptySchema,
6+
EmptySchema,
7+
FlatObject,
8+
Tag,
9+
} from "./common-helpers";
410
import { Endpoint, Handler } from "./endpoint";
511
import { IOSchema, getFinalEndpointInputSchema } from "./io-schema";
612
import { Method } from "./method";
@@ -118,7 +124,7 @@ export class EndpointsFactory<
118124
}
119125

120126
public build<BOUT extends IOSchema, BIN extends IOSchema = EmptySchema>({
121-
input = z.object({}) as unknown as BIN,
127+
input = emptySchema as unknown as BIN,
122128
output: outputSchema,
123129
operationId,
124130
scope,
@@ -152,7 +158,7 @@ export class EndpointsFactory<
152158
}: Omit<BuildProps<BIN, z.ZodVoid, IN, OUT, SCO>, "output">) {
153159
return this.build({
154160
...rest,
155-
output: z.object({}),
161+
output: emptySchema,
156162
handler: async (props) => {
157163
await handler(props);
158164
return {};

express-zod-api/src/middleware.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { NextFunction, Request, Response } from "express";
22
import { z } from "zod/v4";
3-
import { EmptySchema, FlatObject } from "./common-helpers";
3+
import { emptySchema, EmptySchema, FlatObject } from "./common-helpers";
44
import { InputValidationError } from "./errors";
55
import { IOSchema } from "./io-schema";
66
import { LogicalContainer } from "./logical-container";
@@ -50,7 +50,7 @@ export class Middleware<
5050
readonly #handler: Handler<z.output<IN>, OPT, OUT>;
5151

5252
constructor({
53-
input = z.object({}) as unknown as IN,
53+
input = emptySchema as unknown as IN,
5454
security,
5555
handler,
5656
}: {

express-zod-api/tests/__snapshots__/common-helpers.spec.ts.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ exports[`Common Helpers > defaultInputSources > should be declared in a certain
2626
}
2727
`;
2828

29+
exports[`Common Helpers > emptySchema > should be an object schema with empty shape and strip catcher 1`] = `
30+
{
31+
"$schema": "https://json-schema.org/draft/2020-12/schema",
32+
"additionalProperties": false,
33+
"properties": {},
34+
"type": "object",
35+
}
36+
`;
37+
2938
exports[`Common Helpers > getMessageFromError() > should compile a string from ZodError 1`] = `"user.id: expected number, got string; user.name: expected string, got number"`;
3039

3140
exports[`Common Helpers > getMessageFromError() > should handle empty path in ZodIssue 1`] = `"Top level refinement issue"`;

express-zod-api/tests/common-helpers.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,32 @@ import {
77
makeCleanId,
88
ensureError,
99
getRoutePathParams,
10+
emptySchema,
11+
EmptySchema,
12+
EmptyObject,
1013
} from "../src/common-helpers";
1114
import { z } from "zod/v4";
1215
import { makeRequestMock } from "../src/testing";
1316

1417
describe("Common Helpers", () => {
18+
describe("emptySchema", () => {
19+
test("should be an object schema with empty shape and strip catcher", () => {
20+
expect(emptySchema).toMatchSnapshot();
21+
});
22+
});
23+
24+
describe("EmptySchema", () => {
25+
test("should be the type of emptySchema", () => {
26+
expectTypeOf<EmptySchema>().toEqualTypeOf(emptySchema);
27+
});
28+
});
29+
30+
describe("EmptyObject", () => {
31+
test("should be a Record of never", () => {
32+
expectTypeOf<EmptyObject>().toEqualTypeOf<Record<string, never>>();
33+
});
34+
});
35+
1536
describe("defaultInputSources", () => {
1637
test("should be declared in a certain way", () => {
1738
expect(defaultInputSources).toMatchSnapshot();

pnpm-lock.yaml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)