Skip to content

Commit af2fea7

Browse files
authored
patch(return types): union return type with undefined. (#33)
1 parent e4cc854 commit af2fea7

File tree

6 files changed

+387
-303
lines changed

6 files changed

+387
-303
lines changed

examples/react-app/petstore.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,19 @@ paths:
7777
application/json:
7878
schema:
7979
$ref: '#/components/schemas/Error'
80+
/not-defined:
81+
get:
82+
deprecated: true
83+
description: This path is not fully defined.
84+
responses:
85+
default:
86+
description: unexpected error
87+
post:
88+
deprecated: true
89+
description: This path is not defined at all.
90+
responses:
91+
default:
92+
description: unexpected error
8093
/pets/{id}:
8194
get:
8295
description: Returns a user based on a single ID, if the user does not have access to the pet

examples/react-app/src/App.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
useDefaultClientAddPet,
44
useDefaultClientFindPets,
55
useDefaultClientFindPetsKey,
6+
useDefaultClientGetNotDefined,
7+
useDefaultClientPostNotDefined,
68
} from "../openapi/queries";
79
import { useState } from "react";
810
import { queryClient } from "./queryClient";
@@ -13,6 +15,12 @@ function App() {
1315

1416
const { data, error, refetch } = useDefaultClientFindPets({ tags, limit });
1517

18+
// This is an example of a query that is not defined in the OpenAPI spec
19+
// this defaults to any - here we are showing how to override the type
20+
// Note - this is marked as deprecated in the OpenAPI spec and being passed to the client
21+
const { data: notDefined } = useDefaultClientGetNotDefined<undefined>();
22+
const { mutate: mutateNotDefined } = useDefaultClientPostNotDefined<undefined>();
23+
1624
const { mutate: addPet } = useDefaultClientAddPet();
1725

1826
if (error)

src/createExports.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import ts from "typescript";
1+
import ts, { JSDoc } from "typescript";
22
import { sync } from "glob";
33
import { join } from "path";
44
import fs from "fs";
@@ -45,9 +45,27 @@ export const createExports = (generatedClientsPath: string) => {
4545
const httpMethodName = properties
4646
.find((p) => p.name?.getText(node) === "method")
4747
?.initializer?.getText(node)!;
48+
49+
50+
const getAllChildren = (tsNode: ts.Node): Array<ts.Node> => {
51+
const childItems = tsNode.getChildren(node);
52+
if (childItems.length) {
53+
const allChildren = childItems.map(getAllChildren);
54+
return [tsNode].concat(allChildren.flat());
55+
}
56+
return [tsNode];
57+
}
58+
59+
const children = getAllChildren(method);
60+
const jsDoc = children.filter((c) => c.kind === ts.SyntaxKind.JSDoc).map((c) => {
61+
return (c as JSDoc).comment
62+
});
63+
const hasDeprecated = children
64+
.some((c) => c.kind === ts.SyntaxKind.JSDocDeprecatedTag);
65+
4866
return httpMethodName === "'GET'"
49-
? createUseQuery(node, className, method)
50-
: createUseMutation(node, className, method);
67+
? createUseQuery(node, className, method, jsDoc, hasDeprecated)
68+
: createUseMutation(node, className, method, jsDoc, hasDeprecated);
5169
})
5270
.flat();
5371
})

src/createUseMutation.ts

Lines changed: 92 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import ts from "typescript";
22
import { capitalizeFirstLetter } from "./common";
3+
import { addJSDocToNode } from "./util";
34

45
export const createUseMutation = (
56
node: ts.SourceFile,
67
className: string,
7-
method: ts.MethodDeclaration
8+
method: ts.MethodDeclaration,
9+
jsDoc: (string | ts.NodeArray<ts.JSDocComment> | undefined)[] = [],
10+
deprecated: boolean = false
811
) => {
912
const methodName = method.name?.getText(node)!;
1013
// Awaited<ReturnType<typeof myClass.myMethod>>
@@ -26,11 +29,22 @@ export const createUseMutation = (
2629
]
2730
);
2831

32+
const TData = ts.factory.createIdentifier("TData");
33+
const TError = ts.factory.createIdentifier("TError");
34+
const TContext = ts.factory.createIdentifier("TContext");
35+
36+
const mutationResult = ts.factory.createTypeAliasDeclaration(
37+
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
38+
ts.factory.createIdentifier(`${className}${methodName}MutationResult`),
39+
undefined,
40+
awaitedResponseDataType
41+
);
42+
2943
const responseDataType = ts.factory.createTypeParameterDeclaration(
3044
undefined,
31-
"TData",
45+
TData,
3246
undefined,
33-
awaitedResponseDataType
47+
ts.factory.createTypeReferenceNode(mutationResult.name)
3448
);
3549

3650
const methodParameters =
@@ -49,7 +63,7 @@ export const createUseMutation = (
4963
)
5064
: ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
5165

52-
return ts.factory.createVariableStatement(
66+
const exportHook = ts.factory.createVariableStatement(
5367
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
5468
ts.factory.createVariableDeclarationList(
5569
[
@@ -65,13 +79,13 @@ export const createUseMutation = (
6579
responseDataType,
6680
ts.factory.createTypeParameterDeclaration(
6781
undefined,
68-
"TError",
82+
TError,
6983
undefined,
7084
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)
7185
),
7286
ts.factory.createTypeParameterDeclaration(
7387
undefined,
74-
"TContext",
88+
TContext,
7589
undefined,
7690
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)
7791
),
@@ -88,14 +102,10 @@ export const createUseMutation = (
88102
ts.factory.createTypeReferenceNode(
89103
ts.factory.createIdentifier("UseMutationOptions"),
90104
[
91-
awaitedResponseDataType,
92-
ts.factory.createKeywordTypeNode(
93-
ts.SyntaxKind.UnknownKeyword
94-
),
105+
ts.factory.createTypeReferenceNode(TData),
106+
ts.factory.createTypeReferenceNode(TError),
95107
methodParameters,
96-
ts.factory.createKeywordTypeNode(
97-
ts.SyntaxKind.UnknownKeyword
98-
),
108+
ts.factory.createTypeReferenceNode(TContext),
99109
]
100110
),
101111
ts.factory.createLiteralTypeNode(
@@ -108,106 +118,88 @@ export const createUseMutation = (
108118
],
109119
undefined,
110120
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
111-
ts.factory.createAsExpression(
112-
ts.factory.createCallExpression(
113-
ts.factory.createIdentifier("useMutation"),
114-
undefined,
115-
[
116-
ts.factory.createObjectLiteralExpression([
117-
ts.factory.createPropertyAssignment(
118-
ts.factory.createIdentifier("mutationFn"),
119-
ts.factory.createArrowFunction(
120-
undefined,
121-
undefined,
122-
method.parameters.length !== 0
123-
? [
124-
ts.factory.createParameterDeclaration(
125-
undefined,
126-
undefined,
127-
ts.factory.createObjectBindingPattern(
128-
method.parameters.map((param) => {
129-
return ts.factory.createBindingElement(
130-
undefined,
131-
undefined,
132-
ts.factory.createIdentifier(
133-
param.name.getText(node)
134-
),
135-
undefined
136-
);
137-
})
138-
),
139-
undefined,
140-
undefined,
141-
undefined
121+
ts.factory.createCallExpression(
122+
ts.factory.createIdentifier("useMutation"),
123+
[
124+
ts.factory.createTypeReferenceNode(TData),
125+
ts.factory.createTypeReferenceNode(TError),
126+
methodParameters,
127+
ts.factory.createTypeReferenceNode(TContext),
128+
],
129+
[
130+
ts.factory.createObjectLiteralExpression([
131+
ts.factory.createPropertyAssignment(
132+
ts.factory.createIdentifier("mutationFn"),
133+
ts.factory.createArrowFunction(
134+
undefined,
135+
undefined,
136+
method.parameters.length !== 0
137+
? [
138+
ts.factory.createParameterDeclaration(
139+
undefined,
140+
undefined,
141+
ts.factory.createObjectBindingPattern(
142+
method.parameters.map((param) => {
143+
return ts.factory.createBindingElement(
144+
undefined,
145+
undefined,
146+
ts.factory.createIdentifier(
147+
param.name.getText(node)
148+
),
149+
undefined
150+
);
151+
})
142152
),
143-
]
144-
: [],
145-
undefined,
146-
ts.factory.createToken(
147-
ts.SyntaxKind.EqualsGreaterThanToken
148-
),
149-
ts.factory.createCallExpression(
150-
ts.factory.createPropertyAccessExpression(
151-
ts.factory.createIdentifier(className),
152-
ts.factory.createIdentifier(methodName)
153-
),
154-
undefined,
155-
method.parameters.map((params) =>
156-
ts.factory.createIdentifier(
157-
params.name.getText(node)
153+
undefined,
154+
undefined,
155+
undefined
156+
),
157+
]
158+
: [],
159+
undefined,
160+
ts.factory.createToken(
161+
ts.SyntaxKind.EqualsGreaterThanToken
162+
),
163+
ts.factory.createAsExpression(
164+
ts.factory.createAsExpression(
165+
ts.factory.createCallExpression(
166+
ts.factory.createPropertyAccessExpression(
167+
ts.factory.createIdentifier(className),
168+
ts.factory.createIdentifier(methodName)
169+
),
170+
undefined,
171+
method.parameters.map((params) =>
172+
ts.factory.createIdentifier(
173+
params.name.getText(node)
174+
)
158175
)
176+
),
177+
ts.factory.createKeywordTypeNode(
178+
ts.SyntaxKind.UnknownKeyword
159179
)
160-
)
161-
)
162-
),
163-
ts.factory.createSpreadAssignment(
164-
ts.factory.createIdentifier("options")
165-
),
166-
]),
167-
]
168-
),
169-
// Omit<UseMutationResult<Awaited<ReturnType<typeof myClass.myMethod>>, TError, params, TContext>, 'data'> & { data: TData };
170-
ts.factory.createIntersectionTypeNode([
171-
ts.factory.createTypeReferenceNode(
172-
ts.factory.createIdentifier("Omit"),
173-
[
174-
ts.factory.createTypeReferenceNode(
175-
ts.factory.createIdentifier("UseMutationResult"),
176-
[
177-
awaitedResponseDataType,
178-
ts.factory.createTypeReferenceNode(
179-
ts.factory.createIdentifier("TError"),
180-
undefined
181180
),
182-
methodParameters,
181+
183182
ts.factory.createTypeReferenceNode(
184-
ts.factory.createIdentifier("TContext"),
185-
undefined
186-
),
187-
]
188-
),
189-
ts.factory.createLiteralTypeNode(
190-
ts.factory.createStringLiteral("data")
191-
),
192-
]
193-
),
194-
ts.factory.createTypeLiteralNode([
195-
ts.factory.createPropertySignature(
196-
undefined,
197-
ts.factory.createIdentifier("data"),
198-
undefined,
199-
ts.factory.createTypeReferenceNode(
200-
ts.factory.createIdentifier("TData"),
201-
undefined
183+
ts.factory.createIdentifier("Promise"),
184+
[ts.factory.createTypeReferenceNode(TData)]
185+
)
186+
)
202187
)
203188
),
189+
ts.factory.createSpreadAssignment(
190+
ts.factory.createIdentifier("options")
191+
),
204192
]),
205-
])
193+
]
206194
)
207195
)
208196
),
209197
],
210198
ts.NodeFlags.Const
211199
)
212200
);
201+
202+
const hookWithJsDoc = addJSDocToNode(exportHook, node, deprecated, jsDoc);
203+
204+
return [mutationResult, hookWithJsDoc];
213205
};

0 commit comments

Comments
 (0)