Skip to content

Commit 6722c97

Browse files
committed
Actually fix the issue
1 parent d90e35c commit 6722c97

File tree

5 files changed

+25
-54
lines changed

5 files changed

+25
-54
lines changed

packages/openapi-fetch/src/index.d.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {
22
ErrorResponse,
33
FilterKeys,
4+
GetValueWithDefault,
45
HasRequiredKeys,
56
HttpMethod,
67
MediaType,
@@ -114,15 +115,15 @@ export type FetchOptions<T> = RequestOptions<T> &
114115
export type FetchResponse<T, O, Media extends MediaType> =
115116
| {
116117
data: ParseAsResponse<
117-
FilterKeys<SuccessResponse<ResponseObjectMap<T>>, Media>,
118+
GetValueWithDefault<SuccessResponse<ResponseObjectMap<T>>, Media, Record<string, never>>,
118119
O
119120
>;
120121
error?: never;
121122
response: Response;
122123
}
123124
| {
124125
data?: never;
125-
error: FilterKeys<ErrorResponse<ResponseObjectMap<T>>, Media>;
126+
error: GetValueWithDefault<ErrorResponse<ResponseObjectMap<T>>, Media, Record<string, never>>;
126127
response: Response;
127128
};
128129

packages/openapi-fetch/test/fixtures/api.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface paths {
2424
};
2525
responses: {
2626
200: components["responses"]["AllPostsGet"];
27+
401: components["responses"]["EmptyError"];
2728
500: components["responses"]["Error"];
2829
};
2930
};
@@ -457,6 +458,10 @@ export interface components {
457458
"text/html": string;
458459
};
459460
};
461+
EmptyError: {
462+
content: {
463+
};
464+
};
460465
Error: {
461466
content: {
462467
"application/json": {

packages/openapi-fetch/test/fixtures/api.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ paths:
2828
responses:
2929
200:
3030
$ref: '#/components/responses/AllPostsGet'
31+
401:
32+
$ref: '#/components/responses/EmptyError'
3133
500:
3234
$ref: '#/components/responses/Error'
3335
put:
@@ -623,6 +625,8 @@ components:
623625
text/html:
624626
schema:
625627
type: string
628+
EmptyError:
629+
content: {}
626630
Error:
627631
content:
628632
application/json:
+10-52
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,24 @@
11
import { test, expectTypeOf } from "vitest";
22

33
import createClient from "../src/index.js";
4+
import type { paths } from "./fixtures/api.js";
45

5-
interface paths {
6-
"/": {
7-
get: operations["GetObjects"];
8-
};
9-
}
10-
11-
interface operations {
12-
GetObjects: {
13-
parameters: {};
14-
responses: {
15-
200: components["responses"]["MultipleObjectsResponse"];
16-
401: components["responses"]["Unauthorized"];
17-
422: components["responses"]["GenericError"];
18-
};
19-
};
20-
}
6+
const { GET } = createClient<paths>();
217

22-
interface components {
23-
schemas: {
24-
Object: {
25-
id: string;
26-
name: string;
27-
};
28-
GenericErrorModel: {
29-
errors: {
30-
body: string[];
31-
};
32-
};
33-
};
34-
responses: {
35-
MultipleObjectsResponse: {
36-
content: {
37-
"application/json": {
38-
objects: components["schemas"]["Object"][];
39-
};
40-
};
41-
};
42-
/** @description Unauthorized */
43-
Unauthorized: {
44-
content: {};
45-
};
46-
/** @description Unexpected error */
47-
GenericError: {
48-
content: {
49-
"application/json": components["schemas"]["GenericErrorModel"];
50-
};
51-
};
52-
};
8+
interface Blogpost {
9+
title: string;
10+
body: string;
11+
publish_date?: number | undefined;
5312
}
5413

55-
const { GET } = createClient<paths>();
56-
5714
test("the error type works properly", async () => {
58-
const value = await GET("/");
15+
const value = await GET("/blogposts");
5916

6017
if (value.data) {
61-
expectTypeOf(value.data).toEqualTypeOf({ objects: [{ id: "", name: "" }] });
18+
expectTypeOf(value.data).toEqualTypeOf<Array<Blogpost>>();
6219
} else {
6320
expectTypeOf(value.data).toBeUndefined();
64-
expectTypeOf(value.error).toEqualTypeOf({ errors: [""] });
21+
expectTypeOf(value.error).extract<{ code: number }>().toEqualTypeOf<{ code: number; message: string }>();
22+
expectTypeOf(value.error).exclude<{ code: number }>().toEqualTypeOf<Record<string, never>>();
6523
}
6624
});

packages/openapi-typescript-helpers/index.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ export type RequestBodyJSON<PathMethod> = JSONLike<
8888

8989
/** Find first match of multiple keys */
9090
export type FilterKeys<Obj, Matchers> = Obj[keyof Obj & Matchers];
91+
/** Get the type of a value of an input object with a given key. If the key is not found, return a default type. Works with unions of objects too. */
92+
export type GetValueWithDefault<Obj, KeyPattern, Default> = Obj extends any ? (FilterKeys<Obj, KeyPattern> extends never ? Default : FilterKeys<Obj, KeyPattern>) : never;
93+
9194
/** Return any `[string]/[string]` media type (important because openapi-fetch allows any content response, not just JSON-like) */
9295
export type MediaType = `${string}/${string}`;
9396
/** Return any media type containing "json" (works for "application/json", "application/vnd.api+json", "application/vnd.oai.openapi+json") */

0 commit comments

Comments
 (0)