1
- import type { MethodDeclaration } from "ts-morph" ;
1
+ import type { MethodDeclaration , Symbol as TMSymbol } from "ts-morph" ;
2
2
import ts from "typescript" ;
3
3
import {
4
4
BuildCommonTypeName ,
@@ -19,7 +19,10 @@ import { addJSDocToNode } from "./util.mjs";
19
19
export const createApiResponseType = ( {
20
20
className,
21
21
methodName,
22
- } : { className : string ; methodName : string } ) => {
22
+ } : {
23
+ className : string ;
24
+ methodName : string ;
25
+ } ) => {
23
26
/** Awaited<ReturnType<typeof myClass.myMethod>> */
24
27
const awaitedResponseDataType = ts . factory . createTypeReferenceNode (
25
28
ts . factory . createIdentifier ( "Awaited" ) ,
@@ -44,7 +47,9 @@ export const createApiResponseType = ({
44
47
const apiResponse = ts . factory . createTypeAliasDeclaration (
45
48
[ ts . factory . createModifier ( ts . SyntaxKind . ExportKeyword ) ] ,
46
49
ts . factory . createIdentifier (
47
- `${ capitalizeFirstLetter ( className ) } ${ capitalizeFirstLetter ( methodName ) } DefaultResponse` ,
50
+ `${ capitalizeFirstLetter ( className ) } ${ capitalizeFirstLetter (
51
+ methodName ,
52
+ ) } DefaultResponse`,
48
53
) ,
49
54
undefined ,
50
55
awaitedResponseDataType ,
@@ -146,7 +151,9 @@ export function createReturnTypeExport({
146
151
return ts . factory . createTypeAliasDeclaration (
147
152
[ ts . factory . createModifier ( ts . SyntaxKind . ExportKeyword ) ] ,
148
153
ts . factory . createIdentifier (
149
- `${ capitalizeFirstLetter ( className ) } ${ capitalizeFirstLetter ( methodName ) } QueryResult` ,
154
+ `${ capitalizeFirstLetter ( className ) } ${ capitalizeFirstLetter (
155
+ methodName ,
156
+ ) } QueryResult`,
150
157
) ,
151
158
[
152
159
ts . factory . createTypeParameterDeclaration (
@@ -179,7 +186,11 @@ export function createQueryKeyExport({
179
186
className,
180
187
methodName,
181
188
queryKey,
182
- } : { className : string ; methodName : string ; queryKey : string } ) {
189
+ } : {
190
+ className : string ;
191
+ methodName : string ;
192
+ queryKey : string ;
193
+ } ) {
183
194
return ts . factory . createVariableStatement (
184
195
[ ts . factory . createModifier ( ts . SyntaxKind . ExportKeyword ) ] ,
185
196
ts . factory . createVariableDeclarationList (
@@ -201,20 +212,56 @@ export function createQueryKeyExport({
201
212
export function hookNameFromMethod ( {
202
213
method,
203
214
className,
204
- } : { method : MethodDeclaration ; className : string } ) {
215
+ } : {
216
+ method : MethodDeclaration ;
217
+ className : string ;
218
+ } ) {
205
219
const methodName = getNameFromMethod ( method ) ;
206
220
return `use${ className } ${ capitalizeFirstLetter ( methodName ) } ` ;
207
221
}
208
222
209
223
export function createQueryKeyFromMethod ( {
210
224
method,
211
225
className,
212
- } : { method : MethodDeclaration ; className : string } ) {
226
+ } : {
227
+ method : MethodDeclaration ;
228
+ className : string ;
229
+ } ) {
213
230
const customHookName = hookNameFromMethod ( { method, className } ) ;
214
231
const queryKey = `${ customHookName } Key` ;
215
232
return queryKey ;
216
233
}
217
234
235
+ /**
236
+ * Extracts the type of the next page parameter from the given properties.
237
+ *
238
+ * @param properties The properties to search through.
239
+ * @param nextPageParam The name of the next page parameter.
240
+ * @returns The type of the next page parameter, if found.
241
+ */
242
+ function findNextPageParamType (
243
+ properties : TMSymbol [ ] | undefined ,
244
+ nextPageParam : string ,
245
+ ) : string | undefined {
246
+ if ( ! properties ) return undefined ;
247
+
248
+ for ( const property of properties ) {
249
+ if ( property . getName ( ) === nextPageParam ) {
250
+ return property ?. getDeclarations ( ) ?. at ( 0 ) ?. getType ( ) ?. getText ( ) ;
251
+ }
252
+
253
+ const type = property . getDeclarations ( ) . at ( 0 ) ?. getType ( ) ;
254
+ const nestedProperties = type ?. getProperties ( ) ;
255
+
256
+ if ( ! type ?. isObject ( ) || type . isArray ( ) ) continue ;
257
+
258
+ const result = findNextPageParamType ( nestedProperties , nextPageParam ) ;
259
+ if ( result ) return result ;
260
+ }
261
+
262
+ return undefined ;
263
+ }
264
+
218
265
/**
219
266
* Creates a custom hook for a query
220
267
* @param queryString The type of query to use from react-query
@@ -260,6 +307,18 @@ export function createQueryHook({
260
307
const responseDataTypeIdentifier =
261
308
responseDataTypeRef . typeName as ts . Identifier ;
262
309
310
+ const arg = method . getReturnType ( ) . getTypeArguments ( ) . at ( 0 ) ;
311
+
312
+ const nextPageParamTypePropetires = arg ?. getProperties ( ) ;
313
+
314
+ const nextPageParamType =
315
+ arg ?. isObject ( ) && nextPageParam
316
+ ? findNextPageParamType (
317
+ nextPageParamTypePropetires ,
318
+ nextPageParam . split ( "." ) . at ( - 1 ) ?? "" ,
319
+ )
320
+ : undefined ;
321
+
263
322
const hookExport = ts . factory . createVariableStatement (
264
323
[ ts . factory . createModifier ( ts . SyntaxKind . ExportKeyword ) ] ,
265
324
ts . factory . createVariableDeclarationList (
@@ -430,7 +489,11 @@ export function createQueryHook({
430
489
"pageParam" ,
431
490
) ,
432
491
ts . factory . createKeywordTypeNode (
433
- ts . SyntaxKind . NumberKeyword ,
492
+ p . type ?. getText ( ) === "number"
493
+ ? ts . SyntaxKind
494
+ . NumberKeyword
495
+ : ts . SyntaxKind
496
+ . StringKeyword ,
434
497
) ,
435
498
) ,
436
499
)
@@ -453,6 +516,7 @@ export function createQueryHook({
453
516
pageParam ,
454
517
nextPageParam ,
455
518
initialPageParam ,
519
+ nextPageParamType ,
456
520
) ,
457
521
ts . factory . createSpreadAssignment (
458
522
ts . factory . createIdentifier ( "options" ) ,
@@ -637,6 +701,7 @@ function createInfiniteQueryParams(
637
701
pageParam ?: string ,
638
702
nextPageParam ?: string ,
639
703
initialPageParam = "1" ,
704
+ type ?: string ,
640
705
) {
641
706
if ( pageParam === undefined || nextPageParam === undefined ) {
642
707
return [ ] ;
@@ -667,18 +732,23 @@ function createInfiniteQueryParams(
667
732
ts . factory . createParenthesizedExpression (
668
733
ts . factory . createAsExpression (
669
734
ts . factory . createIdentifier ( "response" ) ,
670
- nextPageParam . split ( "." ) . reduceRight ( ( acc , segment ) => {
671
- return ts . factory . createTypeLiteralNode ( [
672
- ts . factory . createPropertySignature (
673
- undefined ,
674
- ts . factory . createIdentifier ( segment ) ,
675
- undefined ,
676
- acc ,
677
- ) ,
678
- ] ) ;
679
- } , ts . factory . createKeywordTypeNode (
680
- ts . SyntaxKind . NumberKeyword ,
681
- ) as ts . TypeNode ) ,
735
+ nextPageParam . split ( "." ) . reduceRight (
736
+ ( acc , segment ) => {
737
+ return ts . factory . createTypeLiteralNode ( [
738
+ ts . factory . createPropertySignature (
739
+ undefined ,
740
+ ts . factory . createIdentifier ( segment ) ,
741
+ undefined ,
742
+ acc ,
743
+ ) ,
744
+ ] ) ;
745
+ } ,
746
+ ts . factory . createKeywordTypeNode (
747
+ type === "number"
748
+ ? ts . SyntaxKind . NumberKeyword
749
+ : ts . SyntaxKind . StringKeyword ,
750
+ ) as ts . TypeNode ,
751
+ ) ,
682
752
) ,
683
753
) ,
684
754
ts . factory . createIdentifier ( nextPageParam ) ,
0 commit comments