Skip to content

Commit

Permalink
fix(context): c.json() allows object and returns JSONParsed<T> (#1806)
Browse files Browse the repository at this point in the history
* fix(context): `c.json()` allows object and returns JSONParsed<T>

* denoify
  • Loading branch information
yusukebe authored Dec 13, 2023
1 parent 87e256b commit af8d16b
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 14 deletions.
8 changes: 4 additions & 4 deletions deno_dist/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { serialize } from './utils/cookie.ts'
import { resolveStream } from './utils/html.ts'
import type { StatusCode } from './utils/http-status.ts'
import { StreamingApi } from './utils/stream.ts'
import type { JSONValue, InterfaceToType } from './utils/types.ts'
import type { JSONValue, InterfaceToType, JSONParsed } from './utils/types.ts'

type HeaderRecord = Record<string, string | string[]>
type Data = string | ArrayBuffer | ReadableStream
Expand Down Expand Up @@ -57,15 +57,15 @@ interface JSONRespond {
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: JSONParsed<T>
: never
>
<T>(object: InterfaceToType<T> extends JSONValue ? T : JSONValue, init?: ResponseInit): Response &
TypedResponse<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: JSONParsed<T>
: never
>
}
Expand Down Expand Up @@ -308,7 +308,7 @@ export class Context<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: JSONParsed<T>
: never
> => {
const body = JSON.stringify(object)
Expand Down
5 changes: 4 additions & 1 deletion deno_dist/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ export type IntersectNonAnyTypes<T extends any[]> = T extends [infer Head, ...in

export type JSONPrimitive = string | boolean | number | null | undefined
export type JSONArray = (JSONPrimitive | JSONObject | JSONArray)[]
export type JSONObject = { [key: string]: JSONPrimitive | JSONArray | JSONObject }
export type JSONObject = { [key: string]: JSONPrimitive | JSONArray | JSONObject | object }
export type JSONValue = JSONObject | JSONArray | JSONPrimitive
export type JSONParsed<T> = {
[k in keyof T]: T[k] extends JSONValue ? T[k] : string
}

export type InterfaceToType<T> = T extends Function ? T : { [K in keyof T]: InterfaceToType<T[K]> }

Expand Down
7 changes: 3 additions & 4 deletions src/client/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,15 +500,14 @@ describe('Merge path with `app.route()`', () => {
expect(data.ok).toBe(true)
})

it('Should not allow the incorrect JSON type', async () => {
it('Should allow a Date object and return it as a string', async () => {
const app = new Hono()
// @ts-expect-error
const route = app.get('/api/foo', (c) => c.json({ datetime: new Date() }))
type AppType = typeof route
const client = hc<AppType>('http://localhost')
const res = await client.api.foo.$get()
const data = await res.json()
type verify = Expect<Equal<never, typeof data>>
const { datetime } = await res.json()
type verify = Expect<Equal<string, typeof datetime>>
})

describe('Multiple endpoints', () => {
Expand Down
8 changes: 4 additions & 4 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { serialize } from './utils/cookie'
import { resolveStream } from './utils/html'
import type { StatusCode } from './utils/http-status'
import { StreamingApi } from './utils/stream'
import type { JSONValue, InterfaceToType } from './utils/types'
import type { JSONValue, InterfaceToType, JSONParsed } from './utils/types'

type HeaderRecord = Record<string, string | string[]>
type Data = string | ArrayBuffer | ReadableStream
Expand Down Expand Up @@ -57,15 +57,15 @@ interface JSONRespond {
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: JSONParsed<T>
: never
>
<T>(object: InterfaceToType<T> extends JSONValue ? T : JSONValue, init?: ResponseInit): Response &
TypedResponse<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: JSONParsed<T>
: never
>
}
Expand Down Expand Up @@ -308,7 +308,7 @@ export class Context<
InterfaceToType<T> extends JSONValue
? JSONValue extends InterfaceToType<T>
? never
: T
: JSONParsed<T>
: never
> => {
const body = JSON.stringify(object)
Expand Down
5 changes: 4 additions & 1 deletion src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ export type IntersectNonAnyTypes<T extends any[]> = T extends [infer Head, ...in

export type JSONPrimitive = string | boolean | number | null | undefined
export type JSONArray = (JSONPrimitive | JSONObject | JSONArray)[]
export type JSONObject = { [key: string]: JSONPrimitive | JSONArray | JSONObject }
export type JSONObject = { [key: string]: JSONPrimitive | JSONArray | JSONObject | object }
export type JSONValue = JSONObject | JSONArray | JSONPrimitive
export type JSONParsed<T> = {
[k in keyof T]: T[k] extends JSONValue ? T[k] : string
}

export type InterfaceToType<T> = T extends Function ? T : { [K in keyof T]: InterfaceToType<T[K]> }

Expand Down

0 comments on commit af8d16b

Please sign in to comment.