-
-
Notifications
You must be signed in to change notification settings - Fork 473
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(openapi-fetch): Return union of possible responses (data & error) #1937
Conversation
🦋 Changeset detectedLatest commit: 080673d The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
// assert 204 to be transformed to empty object | ||
expect(data).toEqual({}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side-note:
I do not fully agree with this behaviour at https://github.com/openapi-ts/openapi-typescript/blob/main/packages/openapi-fetch/src/index.js#L155.
- This makes it impossible to have proper types for 204 codes.
- If users of this package DO return some data from 204's (this shouldn't be the case of course, but if their openapi docs DO specify this then it should still return the data from the response instead of transforming it like this)
This PR ensures that a documented 204 response (content?: never
) will return a data
as a possible undefined
. BUT this will now never be the case because of this behaviour.
Would love to hear some feedback and if it could be considered to remove this 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we’re open to revisiting that behavior! Agree that at this point, that does more harm than good. That was an early behavior that just never received much questioning until now.
I’d be open to either of the following:
- Keep this PR as-is, release as a
patch
, then release that204
change as aminor
(breaking) change, OR - Include that change in this PR, and release everything as a
minor
(breaking) change
Either way it would constitute a breaking change, but IMO a welcome one. No strong preference on #1 or #2, but #1 would be easier to release faster
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My preference would also be to keep this PR as-is aswell. I will make a new PR to fix the 204
thing soon 😄
After that I will have a look at what @zkulbeda suggested for exposing the status
to help narrowing the data
type even more 😎
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That works! Also noticed there’s a tiny conflict with the openapi-typescript-helpers; can merge once that’s resolved.
Also feel free to make an adjustment there if it causes a regression; that recent change was just an arbitrary QoL improvement and didn’t fix any type issues like your PR does
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! Fixed the feedback you gave so everything should be good now 😄
Hello! I have an idea how to improve DX for function's return union type using type narrowing. By refining the const { status, data, error } = await client.GET(pathname);
if (status === 200) {
// TypeScript narrows 'status' to 200, so:
// - 'data' is the type associated with status 200
// - This is more specific than the general 'data' type for all 2xx responses
data; // Autocompletion shows properties specific to 200 responses
} Rough draft of code implementation: diff --git a/packages/openapi-fetch/src/index.d.ts b/packages/openapi-fetch/src/index.d.ts
index 4941612..64395b9 100644
--- a/packages/openapi-fetch/src/index.d.ts
+++ b/packages/openapi-fetch/src/index.d.ts
@@ -1,5 +1,5 @@
import type {
- ErrorResponse,
+ FetchResult,
FilterKeys,
HttpMethod,
IsOperationRequestBodyOptional,
@@ -8,7 +8,6 @@ import type {
PathsWithMethod,
ResponseObjectMap,
RequiredKeysOf,
- SuccessResponse,
} from "openapi-typescript-helpers";
/** Options for each client instance */
@@ -98,17 +97,12 @@ export type RequestBodyOption<T> = OperationRequestBodyContent<T> extends never
export type FetchOptions<T> = RequestOptions<T> & Omit<RequestInit, "body" | "headers">;
-export type FetchResponse<T, Options, Media extends MediaType> =
- | {
- data: ParseAsResponse<SuccessResponse<ResponseObjectMap<T>, Media>, Options>;
- error?: never;
- response: Response;
- }
- | {
- data?: never;
- error: ErrorResponse<ResponseObjectMap<T>, Media>;
- response: Response;
- };
+export type FetchResponse<T, Options, Media extends MediaType> = {
+ response: Response;
+ error?: never;
+ data?: never;
+ status: number;
+} & ParseAsResponse<FetchResult<ResponseObjectMap<T>, Media>, Options>;
export type RequestOptions<T> = ParamsOption<T> &
RequestBodyOption<T> & {
diff --git a/packages/openapi-typescript-helpers/index.d.ts b/packages/openapi-typescript-helpers/index.d.ts
index d8e67c5..f8589c4 100644
--- a/packages/openapi-typescript-helpers/index.d.ts
+++ b/packages/openapi-typescript-helpers/index.d.ts
@@ -122,6 +122,32 @@ export type SuccessResponse<T, Media extends MediaType = MediaType> = FilterKeys
Media
>;
+export type FetchResult<T, Media extends MediaType = MediaType> = {
+ [K in keyof T]: K extends "2XX"
+ ? {
+ status: number;
+ data: FilterKeys<ResponseContent<T[K]>, Media>;
+ error?: never;
+ }
+ : K extends "4XX" | "5XX" | "default"
+ ? {
+ status: number;
+ data?: never;
+ error: FilterKeys<ResponseContent<T[K]>, Media>;
+ }
+ : K extends OkStatus
+ ? {
+ status: K;
+ data: FilterKeys<ResponseContent<T[K]>, Media>;
+ error?: never;
+ }
+ : {
+ status: K;
+ data?: never;
+ error: FilterKeys<ResponseContent<T[K]>, Media>;
+ };
+}[keyof T];
+
/**
* Return first 5XX or 4XX response (in that order) from a Response Object Map
*/
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a FANTASTIC PR, thank you! Great tests that ensure it’s working.
Will wait to merge/release depending on whether or not you’d like to tackle the 204
breaking behavior change as part of this PR, or in a followup. Either works.
// assert 204 to be transformed to empty object | ||
expect(data).toEqual({}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we’re open to revisiting that behavior! Agree that at this point, that does more harm than good. That was an early behavior that just never received much questioning until now.
I’d be open to either of the following:
- Keep this PR as-is, release as a
patch
, then release that204
change as aminor
(breaking) change, OR - Include that change in this PR, and release everything as a
minor
(breaking) change
Either way it would constitute a breaking change, but IMO a welcome one. No strong preference on #1 or #2, but #1 would be easier to release faster
# Conflicts: # packages/openapi-typescript-helpers/index.d.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work. Thanks again! 🙂 Will release this now.
Changes
fixes #1723, fixes #1933, fixes #1820
This change ensures that a union of possible types from the response content is returned.
How to Review
The behaviour of the new type can be seen in the newly added
types.test.ts
file. This also helps ensure possible breaking changes in the future are caught by the failing test(s) (these types are exported after all and can be used by users of this package).Checklist
docs/
updated (if necessary)pnpm run update:examples
run (only applicable for openapi-typescript)