Skip to content

Commit 2ff5756

Browse files
committed
feat: TAllResponses for happy-case narrowing
1 parent 040eb60 commit 2ff5756

24 files changed

+2129
-500
lines changed

packages/typed-openapi/src/generator.ts

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -316,31 +316,42 @@ 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 | number, unknown> = {}> =
320-
| {
321-
ok: true;
322-
status: number;
323-
data: TSuccess;
324-
}
325-
| (keyof TErrors extends never
326-
? never
327-
: {
328-
[K in keyof TErrors]: K extends string
329-
? K extends \`\${infer StatusCode extends number}\`
319+
export type ApiResponse<TSuccess, TAllResponses extends Record<string | number, unknown> = {}> =
320+
(keyof TAllResponses extends never
321+
? {
322+
ok: true;
323+
status: number;
324+
data: TSuccess;
325+
}
326+
: {
327+
[K in keyof TAllResponses]: K extends string
328+
? K extends \`\${infer StatusCode extends number}\`
329+
? StatusCode extends 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308
330330
? {
331+
ok: true;
332+
status: StatusCode;
333+
data: TAllResponses[K];
334+
}
335+
: {
331336
ok: false;
332337
status: StatusCode;
333-
error: TErrors[K];
338+
error: TAllResponses[K];
334339
}
335-
: never
336-
: K extends number
340+
: never
341+
: K extends number
342+
? K extends 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308
337343
? {
344+
ok: true;
345+
status: K;
346+
data: TAllResponses[K];
347+
}
348+
: {
338349
ok: false;
339350
status: K;
340-
error: TErrors[K];
351+
error: TAllResponses[K];
341352
}
342-
: never;
343-
}[keyof TErrors]);
353+
: never;
354+
}[keyof TAllResponses]);
344355
345356
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
346357
? TResponses extends Record<string, unknown>

packages/typed-openapi/tests/generator.test.ts

Lines changed: 216 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -326,31 +326,82 @@ describe("generator", () => {
326326
export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise<Response>;
327327
328328
// Error handling types
329-
export type ApiResponse<TSuccess, TErrors extends Record<string | number, unknown> = {}> =
330-
| {
329+
export type ApiResponse<
330+
TSuccess,
331+
TAllResponses extends Record<string | number, unknown> = {},
332+
> = keyof TAllResponses extends never
333+
? {
331334
ok: true;
332335
status: number;
333336
data: TSuccess;
334337
}
335-
| (keyof TErrors extends never
336-
? never
337-
: {
338-
[K in keyof TErrors]: K extends string
339-
? K extends \`\${infer StatusCode extends number}\`
340-
? {
341-
ok: false;
342-
status: StatusCode;
343-
error: TErrors[K];
344-
}
345-
: never
346-
: K extends number
347-
? {
348-
ok: false;
349-
status: K;
350-
error: TErrors[K];
351-
}
352-
: never;
353-
}[keyof TErrors]);
338+
: {
339+
[K in keyof TAllResponses]: K extends string
340+
? K extends \`\${infer StatusCode extends number}\`
341+
? StatusCode extends
342+
| 200
343+
| 201
344+
| 202
345+
| 203
346+
| 204
347+
| 205
348+
| 206
349+
| 207
350+
| 208
351+
| 226
352+
| 300
353+
| 301
354+
| 302
355+
| 303
356+
| 304
357+
| 305
358+
| 306
359+
| 307
360+
| 308
361+
? {
362+
ok: true;
363+
status: StatusCode;
364+
data: TAllResponses[K];
365+
}
366+
: {
367+
ok: false;
368+
status: StatusCode;
369+
error: TAllResponses[K];
370+
}
371+
: never
372+
: K extends number
373+
? K extends
374+
| 200
375+
| 201
376+
| 202
377+
| 203
378+
| 204
379+
| 205
380+
| 206
381+
| 207
382+
| 208
383+
| 226
384+
| 300
385+
| 301
386+
| 302
387+
| 303
388+
| 304
389+
| 305
390+
| 306
391+
| 307
392+
| 308
393+
? {
394+
ok: true;
395+
status: K;
396+
data: TAllResponses[K];
397+
}
398+
: {
399+
ok: false;
400+
status: K;
401+
error: TAllResponses[K];
402+
}
403+
: never;
404+
}[keyof TAllResponses];
354405
355406
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
356407
? TResponses extends Record<string, unknown>
@@ -937,31 +988,82 @@ describe("generator", () => {
937988
export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise<Response>;
938989
939990
// Error handling types
940-
export type ApiResponse<TSuccess, TErrors extends Record<string | number, unknown> = {}> =
941-
| {
991+
export type ApiResponse<
992+
TSuccess,
993+
TAllResponses extends Record<string | number, unknown> = {},
994+
> = keyof TAllResponses extends never
995+
? {
942996
ok: true;
943997
status: number;
944998
data: TSuccess;
945999
}
946-
| (keyof TErrors extends never
947-
? never
948-
: {
949-
[K in keyof TErrors]: K extends string
950-
? K extends \`\${infer StatusCode extends number}\`
951-
? {
952-
ok: false;
953-
status: StatusCode;
954-
error: TErrors[K];
955-
}
956-
: never
957-
: K extends number
958-
? {
959-
ok: false;
960-
status: K;
961-
error: TErrors[K];
962-
}
963-
: never;
964-
}[keyof TErrors]);
1000+
: {
1001+
[K in keyof TAllResponses]: K extends string
1002+
? K extends \`\${infer StatusCode extends number}\`
1003+
? StatusCode extends
1004+
| 200
1005+
| 201
1006+
| 202
1007+
| 203
1008+
| 204
1009+
| 205
1010+
| 206
1011+
| 207
1012+
| 208
1013+
| 226
1014+
| 300
1015+
| 301
1016+
| 302
1017+
| 303
1018+
| 304
1019+
| 305
1020+
| 306
1021+
| 307
1022+
| 308
1023+
? {
1024+
ok: true;
1025+
status: StatusCode;
1026+
data: TAllResponses[K];
1027+
}
1028+
: {
1029+
ok: false;
1030+
status: StatusCode;
1031+
error: TAllResponses[K];
1032+
}
1033+
: never
1034+
: K extends number
1035+
? K extends
1036+
| 200
1037+
| 201
1038+
| 202
1039+
| 203
1040+
| 204
1041+
| 205
1042+
| 206
1043+
| 207
1044+
| 208
1045+
| 226
1046+
| 300
1047+
| 301
1048+
| 302
1049+
| 303
1050+
| 304
1051+
| 305
1052+
| 306
1053+
| 307
1054+
| 308
1055+
? {
1056+
ok: true;
1057+
status: K;
1058+
data: TAllResponses[K];
1059+
}
1060+
: {
1061+
ok: false;
1062+
status: K;
1063+
error: TAllResponses[K];
1064+
}
1065+
: never;
1066+
}[keyof TAllResponses];
9651067
9661068
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
9671069
? TResponses extends Record<string, unknown>
@@ -1224,31 +1326,82 @@ describe("generator", () => {
12241326
export type Fetcher = (method: Method, url: string, parameters?: EndpointParameters | undefined) => Promise<Response>;
12251327
12261328
// Error handling types
1227-
export type ApiResponse<TSuccess, TErrors extends Record<string | number, unknown> = {}> =
1228-
| {
1329+
export type ApiResponse<
1330+
TSuccess,
1331+
TAllResponses extends Record<string | number, unknown> = {},
1332+
> = keyof TAllResponses extends never
1333+
? {
12291334
ok: true;
12301335
status: number;
12311336
data: TSuccess;
12321337
}
1233-
| (keyof TErrors extends never
1234-
? never
1235-
: {
1236-
[K in keyof TErrors]: K extends string
1237-
? K extends \`\${infer StatusCode extends number}\`
1238-
? {
1239-
ok: false;
1240-
status: StatusCode;
1241-
error: TErrors[K];
1242-
}
1243-
: never
1244-
: K extends number
1245-
? {
1246-
ok: false;
1247-
status: K;
1248-
error: TErrors[K];
1249-
}
1250-
: never;
1251-
}[keyof TErrors]);
1338+
: {
1339+
[K in keyof TAllResponses]: K extends string
1340+
? K extends \`\${infer StatusCode extends number}\`
1341+
? StatusCode extends
1342+
| 200
1343+
| 201
1344+
| 202
1345+
| 203
1346+
| 204
1347+
| 205
1348+
| 206
1349+
| 207
1350+
| 208
1351+
| 226
1352+
| 300
1353+
| 301
1354+
| 302
1355+
| 303
1356+
| 304
1357+
| 305
1358+
| 306
1359+
| 307
1360+
| 308
1361+
? {
1362+
ok: true;
1363+
status: StatusCode;
1364+
data: TAllResponses[K];
1365+
}
1366+
: {
1367+
ok: false;
1368+
status: StatusCode;
1369+
error: TAllResponses[K];
1370+
}
1371+
: never
1372+
: K extends number
1373+
? K extends
1374+
| 200
1375+
| 201
1376+
| 202
1377+
| 203
1378+
| 204
1379+
| 205
1380+
| 206
1381+
| 207
1382+
| 208
1383+
| 226
1384+
| 300
1385+
| 301
1386+
| 302
1387+
| 303
1388+
| 304
1389+
| 305
1390+
| 306
1391+
| 307
1392+
| 308
1393+
? {
1394+
ok: true;
1395+
status: K;
1396+
data: TAllResponses[K];
1397+
}
1398+
: {
1399+
ok: false;
1400+
status: K;
1401+
error: TAllResponses[K];
1402+
}
1403+
: never;
1404+
}[keyof TAllResponses];
12521405
12531406
export type SafeApiResponse<TEndpoint> = TEndpoint extends { response: infer TSuccess; responses: infer TResponses }
12541407
? TResponses extends Record<string, unknown>

0 commit comments

Comments
 (0)