diff --git a/src/index.test.ts b/src/index.test.ts index d63c9fb..e99a547 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -162,11 +162,28 @@ describe('ensureStringBody', () => { }) it('should return the same if body was not defined', () => { - expect(subject.ensureStringBody()).toBe(undefined) + expect(subject.ensureStringBody()).toBeUndefined() }) it('should stringify the body if it is a JSON-like value', () => { expect(subject.ensureStringBody({ page: 2 })).toBe(`{"page":2}`) + expect(subject.ensureStringBody([1, 2])).toBe(`[1,2]`) + expect(subject.ensureStringBody(3)).toBe(`3`) + expect(subject.ensureStringBody(true)).toBe(`true`) + expect(subject.ensureStringBody({})).toBe(`{}`) + }) + + it('should not stringify other valid kinds of BodyInit', () => { + const ab = new ArrayBuffer(0) + expect(subject.ensureStringBody(ab)).toBe(ab) + const rs = new ReadableStream() + expect(subject.ensureStringBody(rs)).toBe(rs) + const fd = new FormData() + expect(subject.ensureStringBody(fd)).toBe(fd) + const usp = new URLSearchParams() + expect(subject.ensureStringBody(usp)).toBe(usp) + const blob = new Blob() + expect(subject.ensureStringBody(blob)).toBe(blob) }) }) diff --git a/src/internals.ts b/src/internals.ts index a782519..63d5939 100644 --- a/src/internals.ts +++ b/src/internals.ts @@ -43,4 +43,32 @@ function replaceUrlParams( return url instanceof URL ? new URL(urlString) : urlString } -export { getJson, getText, replaceUrlParams } +/** + * This is an enhanced version of the typeof operator to check the type of more complex values. + * @param t the value to be checked + * @returns the type of the value + */ +function typeOf(t: unknown) { + return Object.prototype.toString + .call(t) + .replace(/^\[object (.+)\]$/, '$1') + .toLowerCase() as + | 'array' + | 'arraybuffer' + | 'bigint' + | 'blob' + | 'boolean' + | 'formdata' + | 'function' + | 'null' + | 'number' + | 'object' + | 'readablestream' + | 'string' + | 'symbol' + | 'undefined' + | 'url' + | 'urlsearchparams' +} + +export { getJson, getText, replaceUrlParams, typeOf } diff --git a/src/make-service.ts b/src/make-service.ts index a43cd80..2d321ce 100644 --- a/src/make-service.ts +++ b/src/make-service.ts @@ -1,5 +1,5 @@ import { HTTP_METHODS } from './constants' -import { getJson, getText, replaceUrlParams } from './internals' +import { getJson, getText, replaceUrlParams, typeOf } from './internals' import { EnhancedRequestInit, HTTPMethod, @@ -104,12 +104,19 @@ function typedResponse(response: Response): TypedResponse { /** * @param body the JSON-like body of the request - * @returns the body stringified if it is not a string + * @returns the body is stringified if it is not a string and it is a JSON-like object. It also accepts other types of BodyInit such as Blob, ReadableStream, etc. */ -function ensureStringBody(body?: JSONValue): string | undefined { - if (typeof body === 'undefined') return - if (typeof body === 'string') return body - return JSON.stringify(body) +function ensureStringBody( + body?: B, +): B extends JSONValue ? string : B { + if (typeof body === 'undefined') return body as never + if (typeof body === 'string') return body as never + + return ( + ['number', 'boolean', 'array', 'object'].includes(typeOf(body)) + ? JSON.stringify(body) + : body + ) as never } /** diff --git a/src/types.ts b/src/types.ts index 0b61079..d595e1e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,7 +18,7 @@ type TypedResponse = Omit & { } type EnhancedRequestInit = Omit & { - body?: JSONValue + body?: JSONValue | BodyInit | null query?: SearchParams params?: Record trace?: (...args: Parameters) => void