diff --git a/src/index.test.ts b/src/index.test.ts index e5ab94d..c896413 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -351,6 +351,42 @@ describe('post()', () => { // assert error is empty expect(error).toBe(undefined); }); + + it('request body type when optional', async() => { + fetchMocker.mockResponse(() => ({ status: 201, body: '{}' })); + const client = createClient(); + + // expect error on wrong body type + // @ts-expect-error + await client.post('/post/optional', { body: { error: true } }) + + // (no error) + await client.post('/post/optional', { + body: { + title: '', + publish_date: 3, + body: '' + } + }) + }) + + it('request body type when optional inline', async() => { + fetchMocker.mockResponse(() => ({ status: 201, body: '{}' })); + const client = createClient(); + + // expect error on wrong body type + // @ts-expect-error + await client.post('/post/optional/inline', { body: { error: true } }) + + // (no error) + await client.post('/post/optional/inline', { + body: { + title: '', + publish_date: 3, + body: '' + } + }) + }) }); describe('delete()', () => { diff --git a/src/index.ts b/src/index.ts index b8e6582..6373e92 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,16 +34,19 @@ export type FilterKeys = { [K in keyof Obj]: K extends Matchers ? /** handle "application/json", "application/vnd.api+json", "appliacation/json;charset=utf-8" and more */ export type JSONLike = `${string}json${string}`; -// fetch types +// general purpose types export type Params = O extends { parameters: any } ? { params: NonNullable } : BaseParams; -export type RequestBodyObj = O extends { requestBody: any } ? O['requestBody'] : never; +export type RequestBodyObj = O extends { requestBody?: any } ? O['requestBody'] : never; export type RequestBodyContent = undefined extends RequestBodyObj ? FilterKeys>, 'content'> | undefined : FilterKeys, 'content'>; export type RequestBodyJSON = FilterKeys, JSONLike> extends never ? FilterKeys>, JSONLike> | undefined : FilterKeys, JSONLike>; export type RequestBody = undefined extends RequestBodyJSON ? { body?: RequestBodyJSON } : { body: RequestBodyJSON }; export type QuerySerializer = (query: O extends { parameters: { query: any } } ? O['parameters']['query'] : Record) => string; -export type FetchOptions = Params & RequestBody & Omit & { querySerializer?: QuerySerializer }; +export type RequestOptions = Params & RequestBody & { querySerializer?: QuerySerializer }; export type Success = FilterKeys, 'content'>; export type Error = FilterKeys, 'content'>; + +// fetch types +export type FetchOptions = RequestOptions & Omit; export type FetchResponse = | { data: T extends { responses: any } ? NonNullable, JSONLike>> : unknown; error?: never; response: Response } | { data?: never; error: T extends { responses: any } ? NonNullable, JSONLike>> : unknown; response: Response }; diff --git a/test/v1.d.ts b/test/v1.d.ts index 340ee5e..d5a455f 100644 --- a/test/v1.d.ts +++ b/test/v1.d.ts @@ -23,6 +23,28 @@ export interface paths { }; }; }; + "/post/optional": { + post: { + requestBody: components["requestBodies"]["CreatePostOptional"]; + responses: { + 201: components["responses"]["CreatePost"]; + 500: components["responses"]["Error"]; + }; + }; + }; + "/post/optional/inline": { + post: { + requestBody?: { + content: { + "application/json": components["schemas"]["Post"]; + }; + }; + responses: { + 201: components["responses"]["CreatePost"]; + 500: components["responses"]["Error"]; + }; + }; + }; "/posts": { get: { responses: { @@ -271,6 +293,15 @@ export interface components { }; }; }; + CreatePostOptional?: { + content: { + "application/json": { + title: string; + body: string; + publish_date: number; + }; + }; + }; CreateTag?: { content: { "application/json": { diff --git a/test/v1.yaml b/test/v1.yaml index ec8bd58..d1c8277 100644 --- a/test/v1.yaml +++ b/test/v1.yaml @@ -1,4 +1,6 @@ -openapi: +openapi: 3.0.3 +info: + title: Test Specification version: '3.1' paths: /comment: @@ -19,6 +21,27 @@ paths: $ref: '#/components/responses/CreatePost' 500: $ref: '#/components/responses/Error' + /post/optional: + post: + requestBody: + $ref: '#/components/requestBodies/CreatePostOptional' + responses: + 201: + $ref: '#/components/responses/CreatePost' + 500: + $ref: '#/components/responses/Error' + /post/optional/inline: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Post' + responses: + 201: + $ref: '#/components/responses/CreatePost' + 500: + $ref: '#/components/responses/Error' /posts: get: responses: @@ -214,6 +237,23 @@ components: - title - body - publish_date + CreatePostOptional: + required: false + content: + application/json: + schema: + type: object + properties: + title: + type: string + body: + type: string + publish_date: + type: number + required: + - title + - body + - publish_date CreateTag: content: application/json: