Skip to content

Commit 040eb60

Browse files
committed
fix: inference based on error status code
1 parent 27c4afd commit 040eb60

25 files changed

+2269
-1542
lines changed

packages/typed-openapi/src/generator.ts

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -316,19 +316,31 @@ export type Endpoint<TConfig extends DefaultEndpoint = DefaultEndpoint> = {
316316
export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise<Response>;
317317
318318
// Error handling types
319-
export type ApiResponse<TSuccess, TErrors extends Record<string, unknown> = {}> =
319+
export type ApiResponse<TSuccess, TErrors extends Record<string | number, unknown> = {}> =
320320
| {
321321
ok: true;
322322
status: number;
323323
data: TSuccess;
324324
}
325-
| {
326-
[K in keyof TErrors]: {
327-
ok: false;
328-
status: K extends \`\${infer StatusCode extends number}\` ? StatusCode : never;
329-
error: TErrors[K];
330-
};
331-
}[keyof TErrors];
325+
| (keyof TErrors extends never
326+
? never
327+
: {
328+
[K in keyof TErrors]: K extends string
329+
? K extends \`\${infer StatusCode extends number}\`
330+
? {
331+
ok: false;
332+
status: StatusCode;
333+
error: TErrors[K];
334+
}
335+
: never
336+
: K extends number
337+
? {
338+
ok: false;
339+
status: K;
340+
error: TErrors[K];
341+
}
342+
: never;
343+
}[keyof TErrors]);
332344
333345
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
334346
? TResponses extends Record<string, unknown>
@@ -379,34 +391,32 @@ export class ApiClient {
379391
...params: MaybeOptionalArg<${match(ctx.runtime)
380392
.with("zod", "yup", () => infer(`TEndpoint["parameters"]`))
381393
.with("arktype", "io-ts", "typebox", "valibot", () => infer(`TEndpoint`) + `["parameters"]`)
382-
.otherwise(() => `TEndpoint["parameters"]`)}>
394+
.otherwise(() => `TEndpoint["parameters"]`)} & { withResponse?: false }>
383395
): Promise<${match(ctx.runtime)
384396
.with("zod", "yup", () => infer(`TEndpoint["response"]`))
385397
.with("arktype", "io-ts", "typebox", "valibot", () => infer(`TEndpoint`) + `["response"]`)
386398
.otherwise(() => `TEndpoint["response"]`)}>;
387399
388400
${method}<Path extends keyof ${capitalizedMethod}Endpoints, TEndpoint extends ${capitalizedMethod}Endpoints[Path]>(
389401
path: Path,
390-
options: { withResponse: true },
391402
...params: MaybeOptionalArg<${match(ctx.runtime)
392403
.with("zod", "yup", () => infer(`TEndpoint["parameters"]`))
393404
.with("arktype", "io-ts", "typebox", "valibot", () => infer(`TEndpoint`) + `["parameters"]`)
394-
.otherwise(() => `TEndpoint["parameters"]`)}>
405+
.otherwise(() => `TEndpoint["parameters"]`)} & { withResponse: true }>
395406
): Promise<SafeApiResponse<TEndpoint>>;
396407
397408
${method}<Path extends keyof ${capitalizedMethod}Endpoints, TEndpoint extends ${capitalizedMethod}Endpoints[Path]>(
398409
path: Path,
399-
optionsOrParams?: { withResponse?: boolean } | ${match(ctx.runtime)
400-
.with("zod", "yup", () => infer(`TEndpoint["parameters"]`))
401-
.with("arktype", "io-ts", "typebox", "valibot", () => infer(`TEndpoint`) + `["parameters"]`)
402-
.otherwise(() => `TEndpoint["parameters"]`)},
403-
...params: any[]
410+
...params: MaybeOptionalArg<any>
404411
): Promise<any> {
405-
const hasWithResponse = optionsOrParams && typeof optionsOrParams === 'object' && 'withResponse' in optionsOrParams;
406-
const requestParams = hasWithResponse ? params[0] : optionsOrParams;
412+
const requestParams = params[0];
413+
const withResponse = requestParams?.withResponse;
407414
408-
if (hasWithResponse && optionsOrParams.withResponse) {
409-
return this.fetcher("${method}", this.baseUrl + path, requestParams)
415+
// Remove withResponse from params before passing to fetcher
416+
const { withResponse: _, ...fetchParams } = requestParams || {};
417+
418+
if (withResponse) {
419+
return this.fetcher("${method}", this.baseUrl + path, Object.keys(fetchParams).length ? fetchParams : undefined)
410420
.then(async (response) => {
411421
const data = await this.parseResponse(response);
412422
if (response.ok) {
@@ -476,7 +486,7 @@ export function createApiClient(fetcher: Fetcher, baseUrl?: string) {
476486
api.put("/users/:id", { path: { id: 1 }, body: { name: "John" } }).then((user) => console.log(user));
477487
478488
// With error handling
479-
const result = await api.get("/users/{id}", { withResponse: true }, { path: { id: "123" } });
489+
const result = await api.get("/users/{id}", { path: { id: "123" }, withResponse: true });
480490
if (result.ok) {
481491
console.log(result.data);
482492
} else {

0 commit comments

Comments
 (0)