Skip to content

Commit 04a024a

Browse files
committed
Update openapi-fetch types
1 parent 487ef9b commit 04a024a

File tree

6 files changed

+88
-28
lines changed

6 files changed

+88
-28
lines changed

.changeset/beige-ligers-tell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openapi-fetch": patch
3+
---
4+
5+
Fix GET requests requiring 2nd param when it’s not needed

.changeset/rich-poems-swim.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openapi-typescript": minor
3+
---
4+
5+
**Feature**: Added debugger that lets you profile performance and see more in-depth messages

docs/src/content/docs/openapi-fetch/index.md

-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ Next, generate TypeScript types from your OpenAPI schema using openapi-typescrip
6969
npx openapi-typescript ./path/to/api/v1.yaml -o ./src/lib/api/v1.d.ts
7070
```
7171

72-
> ⚠️ Be sure to <a href="https://redocly.com/docs/cli/commands/lint/" target="_blank" rel="noopener noreferrer">validate your schemas</a>! openapi-typescript will err on invalid schemas.
73-
7472
Lastly, be sure to **run typechecking** in your project. This can be done by adding `tsc --noEmit` to your <a href="https://docs.npmjs.com/cli/v9/using-npm/scripts" target="_blank" rel="noopener noreferrer">npm scripts</a> like so:
7573

7674
```json

packages/openapi-fetch/src/index.ts

+23-7
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,46 @@ interface ClientOptions extends Omit<RequestInit, "headers"> {
3232
// headers override to make typing friendlier
3333
headers?: HeadersOptions;
3434
}
35+
3536
export type HeadersOptions =
3637
| HeadersInit
3738
| Record<string, string | number | boolean | null | undefined>;
39+
3840
export type QuerySerializer<T> = (
3941
query: T extends { parameters: any }
4042
? NonNullable<T["parameters"]["query"]>
4143
: Record<string, unknown>,
4244
) => string;
45+
4346
export type BodySerializer<T> = (body: OperationRequestBodyContent<T>) => any;
47+
4448
export type ParseAs = "json" | "text" | "blob" | "arrayBuffer" | "stream";
49+
4550
export interface DefaultParamsOption {
4651
params?: { query?: Record<string, unknown> };
4752
}
53+
54+
export interface EmptyParameters {
55+
query?: never;
56+
header?: never;
57+
path?: never;
58+
cookie?: never;
59+
}
60+
4861
export type ParamsOption<T> = T extends { parameters: any }
49-
? { params: NonNullable<T["parameters"]> }
62+
? T["parameters"] extends EmptyParameters
63+
? DefaultParamsOption
64+
: { params: NonNullable<T["parameters"]> }
5065
: DefaultParamsOption;
66+
5167
export type RequestBodyOption<T> = OperationRequestBodyContent<T> extends never
5268
? { body?: never }
5369
: undefined extends OperationRequestBodyContent<T>
5470
? { body?: OperationRequestBodyContent<T> }
5571
: { body: OperationRequestBodyContent<T> };
72+
5673
export type FetchOptions<T> = RequestOptions<T> & Omit<RequestInit, "body">;
74+
5775
export type FetchResponse<T> =
5876
| {
5977
data: FilterKeys<SuccessResponse<ResponseObjectMap<T>>, MediaType>;
@@ -65,6 +83,7 @@ export type FetchResponse<T> =
6583
error: FilterKeys<ErrorResponse<ResponseObjectMap<T>>, MediaType>;
6684
response: Response;
6785
};
86+
6887
export type RequestOptions<T> = ParamsOption<T> &
6988
RequestBodyOption<T> & {
7089
querySerializer?: QuerySerializer<T>;
@@ -81,7 +100,6 @@ export default function createClient<Paths extends {}>(
81100
bodySerializer: globalBodySerializer,
82101
...options
83102
} = clientOptions;
84-
85103
let baseUrl = options.baseUrl ?? "";
86104
if (baseUrl.endsWith("/")) {
87105
baseUrl = baseUrl.slice(0, -1); // remove trailing slash
@@ -318,11 +336,9 @@ export function createFinalURL<O>(
318336
finalURL = finalURL.replace(`{${k}}`, encodeURIComponent(String(v)));
319337
}
320338
}
321-
if (options.params.query) {
322-
const search = options.querySerializer(options.params.query as any);
323-
if (search) {
324-
finalURL += `?${search}`;
325-
}
339+
const search = options.querySerializer((options.params.query as any) ?? {});
340+
if (search) {
341+
finalURL += `?${search}`;
326342
}
327343
return finalURL;
328344
}

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

+48-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22

33
// HTTP types
44

5-
export type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace";
5+
export type HttpMethod =
6+
| "get"
7+
| "put"
8+
| "post"
9+
| "delete"
10+
| "options"
11+
| "head"
12+
| "patch"
13+
| "trace";
614
/** 2XX statuses */
715
export type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | "2XX";
816
// prettier-ignore
@@ -12,8 +20,15 @@ export type ErrorStatus = 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 |
1220
// OpenAPI type helpers
1321

1422
/** Given an OpenAPI **Paths Object**, find all paths that have the given method */
15-
export type PathsWithMethod<Paths extends Record<string, PathItemObject>, PathnameMethod extends HttpMethod> = {
16-
[Pathname in keyof Paths]: Paths[Pathname] extends { [K in PathnameMethod]: any } ? Pathname : never;
23+
export type PathsWithMethod<
24+
Paths extends Record<string, PathItemObject>,
25+
PathnameMethod extends HttpMethod,
26+
> = {
27+
[Pathname in keyof Paths]: Paths[Pathname] extends {
28+
[K in PathnameMethod]: any;
29+
}
30+
? Pathname
31+
: never;
1732
}[keyof Paths];
1833
/** DO NOT USE! Only used only for OperationObject type inference */
1934
export interface OperationObject {
@@ -23,28 +38,49 @@ export interface OperationObject {
2338
responses: any;
2439
}
2540
/** Internal helper used in PathsWithMethod */
26-
export type PathItemObject = { [M in HttpMethod]: OperationObject } & { parameters?: any };
41+
export type PathItemObject = {
42+
[M in HttpMethod]: OperationObject;
43+
} & { parameters?: any };
2744
/** Return `responses` for an Operation Object */
28-
export type ResponseObjectMap<T> = T extends { responses: any } ? T["responses"] : unknown;
45+
export type ResponseObjectMap<T> = T extends { responses: any }
46+
? T["responses"]
47+
: unknown;
2948
/** Return `content` for a Response Object */
30-
export type ResponseContent<T> = T extends { content: any } ? T["content"] : unknown;
49+
export type ResponseContent<T> = T extends { content: any }
50+
? T["content"]
51+
: unknown;
3152
/** Return `requestBody` for an Operation Object */
32-
export type OperationRequestBody<T> = T extends { requestBody?: any } ? T["requestBody"] : never;
53+
export type OperationRequestBody<T> = T extends { requestBody?: any }
54+
? T["requestBody"]
55+
: never;
3356
/** Internal helper used in OperationRequestBodyContent */
34-
export type OperationRequestBodyMediaContent<T> = undefined extends OperationRequestBody<T> ? FilterKeys<NonNullable<OperationRequestBody<T>>, "content"> | undefined : FilterKeys<OperationRequestBody<T>, "content">;
57+
export type OperationRequestBodyMediaContent<T> =
58+
undefined extends OperationRequestBody<T>
59+
? FilterKeys<NonNullable<OperationRequestBody<T>>, "content"> | undefined
60+
: FilterKeys<OperationRequestBody<T>, "content">;
3561
/** Return first `content` from a Request Object Mapping, allowing any media type */
36-
export type OperationRequestBodyContent<T> = FilterKeys<OperationRequestBodyMediaContent<T>, MediaType> extends never
37-
? FilterKeys<NonNullable<OperationRequestBodyMediaContent<T>>, MediaType> | undefined
62+
export type OperationRequestBodyContent<T> = FilterKeys<
63+
OperationRequestBodyMediaContent<T>,
64+
MediaType
65+
> extends never
66+
?
67+
| FilterKeys<NonNullable<OperationRequestBodyMediaContent<T>>, MediaType>
68+
| undefined
3869
: FilterKeys<OperationRequestBodyMediaContent<T>, MediaType>;
3970
/** Return first 2XX response from a Response Object Map */
4071
export type SuccessResponse<T> = FilterKeys<FilterKeys<T, OkStatus>, "content">;
4172
/** Return first 5XX or 4XX response (in that order) from a Response Object Map */
42-
export type ErrorResponse<T> = FilterKeys<FilterKeys<T, ErrorStatus>, "content">;
73+
export type ErrorResponse<T> = FilterKeys<
74+
FilterKeys<T, ErrorStatus>,
75+
"content"
76+
>;
4377

4478
// Generic TS utils
4579

4680
/** Find first match of multiple keys */
47-
export type FilterKeys<Obj, Matchers> = { [K in keyof Obj]: K extends Matchers ? Obj[K] : never }[keyof Obj];
81+
export type FilterKeys<Obj, Matchers> = {
82+
[K in keyof Obj]: K extends Matchers ? Obj[K] : never;
83+
}[keyof Obj];
4884
/** Return any `[string]/[string]` media type (important because openapi-fetch allows any content response, not just JSON-like) */
4985
export type MediaType = `${string}/${string}`;
5086
/** Filter objects that have required keys */

pnpm-lock.yaml

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)