Skip to content

Commit f682489

Browse files
committed
feat: enhance $Infer handling for schemas, update type resolution logic
1 parent 67fd23c commit f682489

2 files changed

Lines changed: 59 additions & 5 deletions

File tree

packages/better-call/src/endpoint.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,28 @@ describe("types", async () => {
218218
}>();
219219
},
220220
);
221+
222+
// $Infer.body accepts a StandardSchemaV1 schema directly and resolves
223+
// its output type instead of leaking the schema's internal structure.
224+
const bodySchema = z.object({ name: z.string(), age: z.number() });
225+
createEndpoint(
226+
"/path",
227+
{
228+
method: "POST",
229+
body: {},
230+
metadata: {
231+
$Infer: {
232+
body: bodySchema,
233+
},
234+
},
235+
},
236+
async (c) => {
237+
expectTypeOf(c.body).toEqualTypeOf<{
238+
name: string;
239+
age: number;
240+
}>();
241+
},
242+
);
221243
});
222244

223245
it("query", async () => {
@@ -281,6 +303,27 @@ describe("types", async () => {
281303
}>();
282304
},
283305
);
306+
307+
// $Infer.query accepts a StandardSchemaV1 schema directly and resolves
308+
// its output type instead of leaking the schema's internal structure.
309+
const querySchema = z.object({ page: z.number(), limit: z.number() });
310+
createEndpoint(
311+
"/path",
312+
{
313+
method: "GET",
314+
metadata: {
315+
$Infer: {
316+
query: querySchema,
317+
},
318+
},
319+
},
320+
async (c) => {
321+
expectTypeOf(c.query).toEqualTypeOf<{
322+
page: number;
323+
limit: number;
324+
}>();
325+
},
326+
);
284327
});
285328

286329
it("params", async () => {

packages/better-call/src/types.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,24 @@ export type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD";
1717
export type ResolveMethod<M> =
1818
M extends Array<infer U> ? U : M extends "*" ? HTTPMethod : M;
1919

20+
/**
21+
* Resolve a $Infer value: if it's a StandardSchemaV1 schema, extract the
22+
* output type; otherwise use as-is.
23+
*/
24+
type ResolveInferValue<T> = T extends StandardSchemaV1
25+
? StandardSchemaV1.InferOutput<T>
26+
: T;
27+
type ResolveInferValueInput<T> = T extends StandardSchemaV1
28+
? StandardSchemaV1.InferInput<T>
29+
: T;
30+
2031
/**
2132
* Resolves a body schema to its output type.
2233
*/
2334
export type ResolveBody<S, Meta = undefined> = Meta extends {
2435
$Infer: { body: infer B };
2536
}
26-
? B
37+
? ResolveInferValue<B>
2738
: S extends StandardSchemaV1
2839
? StandardSchemaV1.InferOutput<S>
2940
: any;
@@ -34,7 +45,7 @@ export type ResolveBody<S, Meta = undefined> = Meta extends {
3445
export type ResolveQuery<S, Meta = undefined> = Meta extends {
3546
$Infer: { query: infer Q };
3647
}
37-
? Q
48+
? ResolveInferValue<Q>
3849
: S extends StandardSchemaV1
3950
? StandardSchemaV1.InferOutput<S>
4051
: Record<string, any> | undefined;
@@ -45,7 +56,7 @@ export type ResolveQuery<S, Meta = undefined> = Meta extends {
4556
export type ResolveBodyInput<S, Meta = undefined> = Meta extends {
4657
$Infer: { body: infer B };
4758
}
48-
? B
59+
? ResolveInferValueInput<B>
4960
: S extends StandardSchemaV1
5061
? StandardSchemaV1.InferInput<S>
5162
: undefined;
@@ -56,7 +67,7 @@ export type ResolveBodyInput<S, Meta = undefined> = Meta extends {
5667
export type ResolveQueryInput<S, Meta = undefined> = Meta extends {
5768
$Infer: { query: infer Q };
5869
}
59-
? Q
70+
? ResolveInferValueInput<Q>
6071
: S extends StandardSchemaV1
6172
? StandardSchemaV1.InferInput<S>
6273
: Record<string, any> | undefined;
@@ -67,7 +78,7 @@ export type ResolveQueryInput<S, Meta = undefined> = Meta extends {
6778
export type ResolveErrorInput<S, Meta = undefined> = Meta extends {
6879
$Infer: { error: infer E };
6980
}
70-
? E
81+
? ResolveInferValueInput<E>
7182
: S extends StandardSchemaV1
7283
? StandardSchemaV1.InferInput<S>
7384
: undefined;

0 commit comments

Comments
 (0)