Compare commits
2 Commits
8ea85c4aab
...
a8e5f63415
| Author | SHA1 | Date | |
|---|---|---|---|
| a8e5f63415 | |||
| 1cab48ca48 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@robonen/fetch",
|
"name": "@robonen/fetch",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"description": "A lightweight, type-safe fetch wrapper with interceptors, retry, and V8-optimized internals",
|
"description": "A lightweight, type-safe fetch wrapper with interceptors, retry, and V8-optimized internals",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { assertType, describe, expectTypeOf, it } from 'vitest';
|
||||||
|
|
||||||
|
import type { FetchOptions } from './types';
|
||||||
|
import { createFetch } from './fetch';
|
||||||
|
|
||||||
|
type Body = FetchOptions['body'];
|
||||||
|
|
||||||
|
describe('FetchOptions body', () => {
|
||||||
|
it('accepts a named interface without a cast', () => {
|
||||||
|
// The regression under test: a named `interface` has no implicit index
|
||||||
|
// signature, so a `Record<string, unknown>` body type would reject it and
|
||||||
|
// force a cast at every call site. It must assign directly.
|
||||||
|
interface CreateUser { name: string; age: number }
|
||||||
|
expectTypeOf<CreateUser>().toExtend<Body>();
|
||||||
|
assertType<Body>({ name: 'Alice', age: 30 } satisfies CreateUser);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts plain objects, arrays, BodyInit strings and null', () => {
|
||||||
|
assertType<Body>({ a: 1 });
|
||||||
|
assertType<Body>([1, 2, 3]);
|
||||||
|
assertType<Body>('raw string');
|
||||||
|
assertType<Body>(new FormData());
|
||||||
|
assertType<Body>(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects bare primitives', () => {
|
||||||
|
// @ts-expect-error a bare number is not a valid request body
|
||||||
|
assertType<Body>(42);
|
||||||
|
// @ts-expect-error a bare boolean is not a valid request body
|
||||||
|
assertType<Body>(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('lets a typed interface flow through the method shortcuts', () => {
|
||||||
|
interface CreateDeal { title: string; amount: number }
|
||||||
|
const $fetch = createFetch();
|
||||||
|
const deal: CreateDeal = { title: 'x', amount: 1 };
|
||||||
|
// Must type-check with no cast on the body.
|
||||||
|
expectTypeOf($fetch.post).toBeCallableWith('/deals', { body: deal });
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -79,8 +79,14 @@ export interface FetchOptions<R extends ResponseType = 'json', T = unknown>
|
|||||||
FetchHooks<T, R> {
|
FetchHooks<T, R> {
|
||||||
/** Base URL prepended to all relative request URLs */
|
/** Base URL prepended to all relative request URLs */
|
||||||
baseURL?: string;
|
baseURL?: string;
|
||||||
/** Request body — plain objects are automatically JSON-serialized */
|
/**
|
||||||
body?: RequestInit['body'] | Record<string, unknown> | unknown[] | null;
|
* Request body. `BodyInit` values (string, Blob, FormData, streams, …) are
|
||||||
|
* sent as-is; any other object or array is JSON-serialized. Typed as `object`
|
||||||
|
* rather than `Record<string, unknown>` so a named `interface` assigns
|
||||||
|
* directly — interfaces carry no implicit index signature and would otherwise
|
||||||
|
* force every caller to cast the body.
|
||||||
|
*/
|
||||||
|
body?: RequestInit['body'] | object | null;
|
||||||
/** Suppress throwing on 4xx/5xx responses */
|
/** Suppress throwing on 4xx/5xx responses */
|
||||||
ignoreResponseError?: boolean;
|
ignoreResponseError?: boolean;
|
||||||
/** URL query parameters serialized and appended to the request URL */
|
/** URL query parameters serialized and appended to the request URL */
|
||||||
|
|||||||
@@ -3,5 +3,11 @@ import { defineConfig } from 'vitest/config';
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
test: {
|
test: {
|
||||||
environment: 'node',
|
environment: 'node',
|
||||||
|
// Type tests (*.test-d.ts) are statically analyzed with `tsc --noEmit`
|
||||||
|
// alongside the runtime suite.
|
||||||
|
typecheck: {
|
||||||
|
enabled: true,
|
||||||
|
tsconfig: './tsconfig.src.json',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user