diff --git a/.changeset/warm-plants-thank.md b/.changeset/warm-plants-thank.md new file mode 100644 index 000000000..aa241c8e8 --- /dev/null +++ b/.changeset/warm-plants-thank.md @@ -0,0 +1,5 @@ +--- +'@hono/valibot-validator': patch +--- + +Fix request query types for valibot schemas diff --git a/packages/valibot-validator/src/index.ts b/packages/valibot-validator/src/index.ts index 75fb4a7c2..438cbc959 100644 --- a/packages/valibot-validator/src/index.ts +++ b/packages/valibot-validator/src/index.ts @@ -1,4 +1,4 @@ -import type { Context, Env, Input as HonoInput, MiddlewareHandler, ValidationTargets } from 'hono' +import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono' import { validator } from 'hono/validator' import type { GenericSchema, @@ -9,10 +9,18 @@ import type { } from 'valibot' import { safeParseAsync } from 'valibot' -export type Hook = ( - result: SafeParseResult, +export type Hook< + T extends GenericSchema | GenericSchemaAsync, + E extends Env, + P extends string, + Target extends keyof ValidationTargets = keyof ValidationTargets, + O = {} +> = ( + result: SafeParseResult & { + target: Target + }, c: Context -) => Response | Promise | void | Promise +) => Response | void | TypedResponse | Promise> type HasUndefined = undefined extends T ? true : false @@ -23,20 +31,16 @@ export const vValidator = < P extends string, In = InferInput, Out = InferOutput, - I extends HonoInput = { + I extends Input = { in: HasUndefined extends true ? { - [K in Target]?: K extends 'json' + [K in Target]?: In extends ValidationTargets[K] ? In - : HasUndefined extends true - ? { [K2 in keyof In]?: ValidationTargets[K][K2] } - : { [K2 in keyof In]: ValidationTargets[K][K2] } + : { [K2 in keyof In]?: ValidationTargets[K][K2] } } : { - [K in Target]: K extends 'json' + [K in Target]: In extends ValidationTargets[K] ? In - : HasUndefined extends true - ? { [K2 in keyof In]?: ValidationTargets[K][K2] } : { [K2 in keyof In]: ValidationTargets[K][K2] } } out: { [K in Target]: Out } @@ -45,16 +49,22 @@ export const vValidator = < >( target: Target, schema: T, - hook?: Hook + hook?: Hook ): MiddlewareHandler => // @ts-expect-error not typed well validator(target, async (value, c) => { const result = await safeParseAsync(schema, value) if (hook) { - const hookResult = hook(result, c) - if (hookResult instanceof Response || hookResult instanceof Promise) { - return hookResult + const hookResult = await hook({ ...result, target }, c) + if (hookResult) { + if (hookResult instanceof Response) { + return hookResult + } + + if ('response' in hookResult) { + return hookResult.response + } } } @@ -62,6 +72,5 @@ export const vValidator = < return c.json(result, 400) } - const data = result.output as InferOutput - return data + return result.output }) diff --git a/packages/valibot-validator/test/index.test.ts b/packages/valibot-validator/test/index.test.ts index d50fd78e9..2c16d7616 100644 --- a/packages/valibot-validator/test/index.test.ts +++ b/packages/valibot-validator/test/index.test.ts @@ -324,3 +324,28 @@ describe('With Hook Async', () => { expect(res.status).toBe(400) }) }) + +describe('Test types', () => { + it('Should return correct types when validating a query', () => { + const app = new Hono() + + const routes = app.post( + '/', + vValidator( + 'query', + object({ + foo: string(), + }) + ), + (c) => { + return c.json(c.req.valid('query')) + } + ) + + type T = ExtractSchema + + type Actual = T['/']['$post']['input']['query'] + type Expected = { foo: string } + type verify = Expect> + }) +})