0
0
mirror of https://github.com/honojs/hono.git synced 2024-12-01 11:51:01 +01:00

feat: Allow context.jsonT to take interface as an argument (#1162)

This commit is contained in:
ayame113 2023-06-06 10:23:09 +09:00 committed by GitHub
parent aaa1c6d4b9
commit 81d271279c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 103 additions and 10 deletions

View File

@ -4,7 +4,7 @@ import type { Env, NotFoundHandler, Input } from './types.ts'
import type { CookieOptions } from './utils/cookie.ts'
import { serialize } from './utils/cookie.ts'
import type { StatusCode } from './utils/http-status.ts'
import type { JSONValue } from './utils/types.ts'
import type { JSONValue, InterfaceToType } from './utils/types.ts'
type Runtime = 'node' | 'deno' | 'bun' | 'workerd' | 'fastly' | 'edge-light' | 'lagon' | 'other'
type HeaderRecord = Record<string, string | string[]>
@ -48,9 +48,40 @@ interface JSONTRespond {
object: T extends JSONValue ? T : JSONValue,
status?: StatusCode,
headers?: HeaderRecord
): TypedResponse<T extends JSONValue ? (JSONValue extends T ? never : T) : never>
): TypedResponse<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: never
>
<T>(
object: InterfaceToType<T> extends JSONValue ? T : JSONValue,
status?: StatusCode,
headers?: HeaderRecord
): TypedResponse<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: never
>
<T>(object: T extends JSONValue ? T : JSONValue, init?: ResponseInit): TypedResponse<
T extends JSONValue ? (JSONValue extends T ? never : T) : never
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: never
>
<T>(
object: InterfaceToType<T> extends JSONValue ? T : JSONValue,
init?: ResponseInit
): TypedResponse<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: never
>
}
@ -309,10 +340,16 @@ export class Context<
}
jsonT: JSONTRespond = <T>(
object: T extends JSONValue ? T : JSONValue,
object: InterfaceToType<T> extends JSONValue ? T : JSONValue,
arg?: StatusCode | RequestInit,
headers?: HeaderRecord
): TypedResponse<T extends JSONValue ? (JSONValue extends T ? never : T) : never> => {
): TypedResponse<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: never
> => {
return {
response: typeof arg === 'number' ? this.json(object, arg, headers) : this.json(object, arg),
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@ -21,3 +21,5 @@ export type JSONPrimitive = string | boolean | number | null | undefined
export type JSONArray = (JSONPrimitive | JSONObject | JSONArray)[]
export type JSONObject = { [key: string]: JSONPrimitive | JSONArray | JSONObject }
export type JSONValue = JSONObject | JSONArray | JSONPrimitive
export type InterfaceToType<T> = T extends Function ? T : { [K in keyof T]: InterfaceToType<T[K]> }

View File

@ -359,6 +359,21 @@ describe('Merge path with `app.route()`', () => {
expect(data.ok).toBe(true)
})
it('Should have correct types - with interface', async () => {
interface Result {
ok: boolean
}
const result: Result = { ok: true }
const base = new Hono<Env>().basePath('/api')
const app = base.get('/search', (c) => c.jsonT(result))
type AppType = typeof app
const client = hc<AppType>('http://localhost')
const res = await client.api.search.$get()
const data = await res.json()
type verify = Expect<Equal<Result, typeof data>>
expect(data.ok).toBe(true)
})
it('Should not allow the incorrect JSON type', async () => {
const app = new Hono()
// @ts-ignore

View File

@ -4,7 +4,7 @@ import type { Env, NotFoundHandler, Input } from './types'
import type { CookieOptions } from './utils/cookie'
import { serialize } from './utils/cookie'
import type { StatusCode } from './utils/http-status'
import type { JSONValue } from './utils/types'
import type { JSONValue, InterfaceToType } from './utils/types'
type Runtime = 'node' | 'deno' | 'bun' | 'workerd' | 'fastly' | 'edge-light' | 'lagon' | 'other'
type HeaderRecord = Record<string, string | string[]>
@ -48,9 +48,40 @@ interface JSONTRespond {
object: T extends JSONValue ? T : JSONValue,
status?: StatusCode,
headers?: HeaderRecord
): TypedResponse<T extends JSONValue ? (JSONValue extends T ? never : T) : never>
): TypedResponse<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: never
>
<T>(
object: InterfaceToType<T> extends JSONValue ? T : JSONValue,
status?: StatusCode,
headers?: HeaderRecord
): TypedResponse<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: never
>
<T>(object: T extends JSONValue ? T : JSONValue, init?: ResponseInit): TypedResponse<
T extends JSONValue ? (JSONValue extends T ? never : T) : never
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: never
>
<T>(
object: InterfaceToType<T> extends JSONValue ? T : JSONValue,
init?: ResponseInit
): TypedResponse<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: never
>
}
@ -309,10 +340,16 @@ export class Context<
}
jsonT: JSONTRespond = <T>(
object: T extends JSONValue ? T : JSONValue,
object: InterfaceToType<T> extends JSONValue ? T : JSONValue,
arg?: StatusCode | RequestInit,
headers?: HeaderRecord
): TypedResponse<T extends JSONValue ? (JSONValue extends T ? never : T) : never> => {
): TypedResponse<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: never
> => {
return {
response: typeof arg === 'number' ? this.json(object, arg, headers) : this.json(object, arg),
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@ -21,3 +21,5 @@ export type JSONPrimitive = string | boolean | number | null | undefined
export type JSONArray = (JSONPrimitive | JSONObject | JSONArray)[]
export type JSONObject = { [key: string]: JSONPrimitive | JSONArray | JSONObject }
export type JSONValue = JSONObject | JSONArray | JSONPrimitive
export type InterfaceToType<T> = T extends Function ? T : { [K in keyof T]: InterfaceToType<T[K]> }