Skip to content

Commit b588d2c

Browse files
committed
Improve z.function typing. Add .def
1 parent 7bd15ed commit b588d2c

File tree

4 files changed

+64
-47
lines changed

4 files changed

+64
-47
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ test("function inference 1", () => {
6868
test("args method", () => {
6969
const t1 = z.function();
7070
type t1 = (typeof t1)["_input"];
71-
expectTypeOf<t1>().toEqualTypeOf<(...args_1: unknown[]) => unknown>();
71+
expectTypeOf<t1>().toEqualTypeOf<(...args_1: never[]) => unknown>();
7272

7373
const t2args = z.tuple([z.string()], z.unknown());
7474

@@ -81,6 +81,14 @@ test("args method", () => {
8181
expectTypeOf<t3>().toEqualTypeOf<(arg: string, ...args_1: unknown[]) => boolean>();
8282
});
8383

84+
test("contravariance", () => {
85+
const fn = z.function().implement((_a: string, _b: number) => {
86+
return new Date();
87+
});
88+
89+
expectTypeOf(fn).toEqualTypeOf<(a: string, b: number) => Date>();
90+
});
91+
8492
const args2 = z.tuple([
8593
z.object({
8694
f1: z.number(),

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

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,47 @@ import type * as util from "./util.js";
1212
////////// //////////
1313
//////////////////////////////////////////
1414
//////////////////////////////////////////
15-
export interface $ZodFunctionDef /* extends schemas.$ZodTypeDef */ {
15+
export interface $ZodFunctionDef<
16+
In extends $ZodFunctionIn = $ZodFunctionIn,
17+
Out extends $ZodFunctionOut = $ZodFunctionOut,
18+
> {
1619
type: "function";
17-
input: $ZodFunctionArgs | null;
18-
output: schemas.$ZodType | null;
20+
input: In;
21+
output: Out;
1922
}
2023

2124
export type $ZodFunctionArgs = schemas.$ZodType<unknown[], unknown[]>;
25+
export type $ZodFunctionIn = $ZodFunctionArgs | null;
26+
export type $ZodFunctionOut = schemas.$ZodType | null;
2227

23-
export type $InferInnerFunctionType<Args extends $ZodFunctionArgs, Returns extends schemas.$ZodType> = (
24-
...args: Args["_zod"]["output"]
25-
) => Returns["_zod"]["input"];
28+
export type $InferInnerFunctionType<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
29+
...args: null extends Args ? never[] : NonNullable<Args>["_zod"]["output"]
30+
) => null extends Returns ? unknown : NonNullable<Returns>["_zod"]["input"];
2631

27-
export type $InferInnerFunctionTypeAsync<Args extends $ZodFunctionArgs, Returns extends schemas.$ZodType> = (
28-
...args: Args["_zod"]["output"]
29-
) => util.MaybeAsync<Returns["_zod"]["input"]>;
32+
export type $InferInnerFunctionTypeAsync<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
33+
...args: null extends Args ? never[] : NonNullable<Args>["_zod"]["output"]
34+
) => null extends Returns ? unknown : util.MaybeAsync<NonNullable<Returns>["_zod"]["input"]>;
3035

31-
export type $InferOuterFunctionType<Args extends $ZodFunctionArgs, Returns extends schemas.$ZodType> = (
32-
...args: Args["_zod"]["input"]
33-
) => Returns["_zod"]["output"];
36+
export type $InferOuterFunctionType<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
37+
...args: null extends Args ? never[] : NonNullable<Args>["_zod"]["input"]
38+
) => null extends Returns ? unknown : NonNullable<Returns>["_zod"]["output"];
3439

35-
export type $InferOuterFunctionTypeAsync<Args extends $ZodFunctionArgs, Returns extends schemas.$ZodType> = (
36-
...args: Args["_zod"]["input"]
37-
) => util.MaybeAsync<Returns["_zod"]["output"]>;
40+
export type $InferOuterFunctionTypeAsync<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
41+
...args: null extends Args ? never[] : NonNullable<Args>["_zod"]["input"]
42+
) => null extends Returns ? unknown : util.MaybeAsync<NonNullable<Returns>["_zod"]["output"]>;
3843

3944
export class $ZodFunction<
40-
Args extends $ZodFunctionArgs = $ZodFunctionArgs,
41-
Returns extends schemas.$ZodType = schemas.$ZodType,
45+
Args extends $ZodFunctionIn = $ZodFunctionIn,
46+
Returns extends $ZodFunctionOut = $ZodFunctionOut,
4247
> {
43-
_def: $ZodFunctionDef;
48+
_def: $ZodFunctionDef<Args, Returns>;
49+
def: $ZodFunctionDef<Args, Returns>;
4450
_input!: $InferInnerFunctionType<Args, Returns>;
4551
_output!: $InferOuterFunctionType<Args, Returns>;
4652

47-
constructor(def: $ZodFunctionDef) {
53+
constructor(def: $ZodFunctionDef<Args, Returns>) {
4854
this._def = def;
55+
this.def = def;
4956
}
5057

5158
implement<F extends $InferInnerFunctionType<Args, Returns>>(
@@ -59,7 +66,7 @@ export class $ZodFunction<
5966
if (!Array.isArray(parsedArgs)) {
6067
throw new Error("Invalid arguments schema: not an array or tuple schema.");
6168
}
62-
const output = func(...parsedArgs);
69+
const output = func(...(parsedArgs as any));
6370
return this._def.output ? parse(this._def.output, output, undefined, { callee: impl }) : output;
6471
}) as any;
6572
return impl;
@@ -77,17 +84,17 @@ export class $ZodFunction<
7784
if (!Array.isArray(parsedArgs)) {
7885
throw new Error("Invalid arguments schema: not an array or tuple schema.");
7986
}
80-
const output = await func(...parsedArgs);
87+
const output = await func(...(parsedArgs as any));
8188
return this._def.output ? parseAsync(this._def.output, output, undefined, { callee: impl }) : output;
8289
}) as any;
8390
return impl;
8491
}
8592

86-
input<const Items extends util.TupleItems, const Rest extends schemas.$ZodType | null = null>(
93+
input<const Items extends util.TupleItems, const Rest extends $ZodFunctionOut = null>(
8794
args: Items,
8895
rest?: Rest
8996
): $ZodFunction<schemas.$ZodTuple<Items, Rest>, Returns>;
90-
input<NewArgs extends $ZodFunctionArgs>(args: NewArgs): $ZodFunction<NewArgs, Returns>;
97+
input<NewArgs extends $ZodFunctionIn>(args: NewArgs): $ZodFunction<NewArgs, Returns>;
9198
input(...args: any[]): $ZodFunction<any, Returns> {
9299
if (Array.isArray(args[0])) {
93100
return new $ZodFunction({
@@ -116,19 +123,28 @@ export class $ZodFunction<
116123
}
117124
}
118125

119-
export interface $ZodFunctionParams<I extends $ZodFunctionArgs, O extends schemas.$ZodType> {
126+
export interface $ZodFunctionParams<I extends $ZodFunctionIn, O extends schemas.$ZodType> {
120127
input?: I;
121128
output?: O;
122129
}
123130

124131
function _function(): $ZodFunction;
132+
function _function<const In extends Array<schemas.$ZodType> = Array<schemas.$ZodType>>(params: {
133+
input: In;
134+
}): $ZodFunction<$ZodTuple<In, null>, null>;
135+
function _function<const In extends $ZodFunctionIn = $ZodFunctionIn>(params: {
136+
input: In;
137+
}): $ZodFunction<In, null>;
138+
function _function<const Out extends $ZodFunctionOut = $ZodFunctionOut>(params: {
139+
output: Out;
140+
}): $ZodFunction<null, Out>;
125141
function _function<
126-
const In extends Array<schemas.$ZodType> = Array<schemas.$ZodType>,
142+
In extends $ZodFunctionIn = $ZodFunctionIn,
127143
Out extends schemas.$ZodType = schemas.$ZodType,
128-
>(params?: { input?: In; output?: Out }): $ZodFunction<$ZodTuple<In, null>, Out>;
129-
function _function<In extends $ZodFunctionArgs = $ZodFunctionArgs, Out extends schemas.$ZodType = schemas.$ZodType>(
130-
params?: $ZodFunctionParams<In, Out>
131-
): $ZodFunction<In, Out>;
144+
>(params?: {
145+
input: In;
146+
output: Out;
147+
}): $ZodFunction<In, Out>;
132148
function _function(params?: {
133149
output?: schemas.$ZodType;
134150
input?: $ZodFunctionArgs | Array<schemas.$ZodType>;

play.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
import * as z from "zod/v4";
22

3-
z;
3+
export const a = z.object({
4+
asdf: z.iso.datetime(),
5+
});
46

5-
const result = z.toJSONSchema(z.date(), {
6-
unrepresentable: "any",
7-
override: (ctx) => {
8-
const def = ctx.zodSchema._zod.def;
9-
if (def.type === "date") {
10-
ctx.jsonSchema.type = "string";
11-
ctx.jsonSchema.format = "date-time";
12-
}
13-
},
7+
const fn = z.function({
8+
// input: z.tuple([z.string()], z.number()),
9+
output: z.string(),
1410
});
15-
/* => {
16-
type: 'string',
17-
format: 'date-time',
18-
'$schema': 'https://json-schema.org/draft/2020-12/schema'
19-
} */
20-
console.dir(result, { depth: null });
11+
12+
fn.def.output;
13+
fn.def.input;

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"extends": "./.configs/tsconfig.base.json",
33
"compilerOptions": {
44
"rootDir": ".",
5-
"noEmit": true
5+
"declaration": true
66
},
77
"exclude": ["packages/docs"]
88
}

0 commit comments

Comments
 (0)