Skip to content

Commit

Permalink
Merge pull request #25 from gustavoguichard/typed-response-options
Browse files Browse the repository at this point in the history
Expose getJson and getText as typedResponse options
  • Loading branch information
danielweinmann authored Jun 2, 2023
2 parents 35a8152 + 2f81e1b commit a6da4a7
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 18 deletions.
22 changes: 16 additions & 6 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ import {
mergeHeaders,
replaceURLParams,
} from './primitives'
import {
import type {
BaseOptions,
EnhancedRequestInit,
GetJson,
GetText,
HTTPMethod,
ServiceRequestInit,
TypedResponse,
TypedResponseJson,
TypedResponseText,
} from './types'

const identity = <T>(value: T) => value
Expand All @@ -31,16 +35,22 @@ const identity = <T>(value: T) => value
* const typedJson = await response.json<User[]>();
* // ^? User[]
*/
function typedResponse(response: Response): TypedResponse {
function typedResponse(
response: Response,
options?: { getJson?: GetJson; getText?: GetText },
): TypedResponse {
const getJsonFn = options?.getJson ?? getJson
const getTextFn = options?.getText ?? getText

return new Proxy(response, {
get(target, prop) {
if (prop === 'json') return getJson(target)
if (prop === 'text') return getText(target)
if (prop === 'json') return getJsonFn(target)
if (prop === 'text') return getTextFn(target)
return target[prop as keyof Response]
},
}) as Omit<Response, 'json' | 'text'> & {
json: ReturnType<typeof getJson>
text: ReturnType<typeof getText>
json: TypedResponseJson
text: TypedResponseText
}
}

Expand Down
14 changes: 7 additions & 7 deletions src/internals.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { Schema } from './types'
import type { GetJson, GetText, Schema } from './types'

/**
* It returns the JSON object or throws an error if the response is not ok.
* @param response the Response to be parsed
* @returns the response.json method that accepts a type or Zod schema for a typed json response
*/
function getJson(response: Response) {
return async <T = unknown>(schema?: Schema<T>): Promise<T> => {
const getJson: GetJson =
(response) =>
async <T = unknown>(schema?: Schema<T>) => {
const json = await response.json()
return schema ? schema.parse(json) : (json as T)
}
}

/**
* @param response the Response to be parsed
* @returns the response.text method that accepts a type or Zod schema for a typed response
*/
function getText(response: Response) {
return async <T extends string = string>(schema?: Schema<T>): Promise<T> => {
const getText: GetText =
(response) =>
async <T extends string = string>(schema?: Schema<T>) => {
const text = await response.text()
return schema ? schema.parse(text) : (text as T)
}
}

/**
* This is an enhanced version of the typeof operator to check the type of more complex values.
Expand Down
27 changes: 22 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { HTTP_METHODS } from './constants'
import { getJson, getText } from './internals'

type Schema<T> = { parse: (d: unknown) => T }

Expand Down Expand Up @@ -33,16 +32,30 @@ type EnhancedRequestInit<T = string> = Omit<RequestInit, 'body' | 'method'> & {

type ServiceRequestInit<T = string> = Omit<EnhancedRequestInit<T>, 'method'>

type RequestTransformer = (
request: EnhancedRequestInit,
) => EnhancedRequestInit | Promise<EnhancedRequestInit>

type ResponseTransformer = (
response: TypedResponse,
) => TypedResponse | Promise<TypedResponse>

type BaseOptions = {
headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>)
requestTransformer?: (request: EnhancedRequestInit) => EnhancedRequestInit | Promise<EnhancedRequestInit>
responseTransformer?: (response: TypedResponse) => TypedResponse | Promise<TypedResponse>
requestTransformer?: RequestTransformer
responseTransformer?: ResponseTransformer
}

type HTTPMethod = (typeof HTTP_METHODS)[number]

type TypedResponseJson = ReturnType<typeof getJson>
type TypedResponseText = ReturnType<typeof getText>
type TypedResponseJson = <T = unknown>(schema?: Schema<T>) => Promise<T>

type TypedResponseText = <T extends string = string>(
schema?: Schema<T>,
) => Promise<T>

type GetJson = (response: Response) => TypedResponseJson
type GetText = (response: Response) => TypedResponseText

type Prettify<T> = {
[K in keyof T]: T[K]
Expand All @@ -57,6 +70,8 @@ type ExtractPathParams<T extends string> =

export type {
EnhancedRequestInit,
GetJson,
GetText,
HTTPMethod,
JSONValue,
PathParams,
Expand All @@ -67,4 +82,6 @@ export type {
TypedResponse,
TypedResponseJson,
TypedResponseText,
RequestTransformer,
ResponseTransformer,
}
1 change: 1 addition & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export default defineConfig(() => ({
maxConcurrency: 1,
minThreads: 0,
maxThreads: 1,
exclude: ['tsc', 'node_modules'],
},
}))

0 comments on commit a6da4a7

Please sign in to comment.