Skip to content

Commit 99c88a5

Browse files
committed
feat: consider array return type in visitor
1 parent d7fa31e commit 99c88a5

File tree

4 files changed

+104
-1
lines changed

4 files changed

+104
-1
lines changed

lib/fixtures/fake-service-code.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,26 @@ class UserService {
4747
throw new Error('not implemented');
4848
}
4949
}`;
50+
51+
export const arrayResponseBodyServiceCode = `
52+
import { HttpInterface, GetExchange } from '@r2don/nest-http-interface';
53+
54+
class ResponseClass {}
55+
56+
@HttpInterface()
57+
class UserService {
58+
@GetExchange()
59+
async getUsers(): Promise<ResponseClass[]> {
60+
throw new Error('not implemented');
61+
}
62+
63+
@GetExchange()
64+
async getUserList(): Promise<Array<ResponseClass>> {
65+
throw new Error('not implemented');
66+
}
67+
68+
@GetExchange()
69+
async getUsersReadonly(): Promise<readonly ResponseClass[]> {
70+
throw new Error('not implemented');
71+
}
72+
}`;

lib/plugin/visitors/__snapshots__/http-interface.visitor.spec.ts.snap

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,38 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`HttpInterfaceVisitor > should handle array return type 1`] = `
4+
"import { HttpInterface, GetExchange } from '@r2don/nest-http-interface';
5+
class ResponseClass {
6+
}
7+
let UserService = class UserService {
8+
async getUsers() {
9+
throw new Error('not implemented');
10+
}
11+
async getUserList() {
12+
throw new Error('not implemented');
13+
}
14+
async getUsersReadonly() {
15+
throw new Error('not implemented');
16+
}
17+
};
18+
__decorate([
19+
GetExchange(),
20+
ResponseBody(ResponseClass)
21+
], UserService.prototype, \\"getUsers\\", null);
22+
__decorate([
23+
GetExchange(),
24+
ResponseBody(ResponseClass)
25+
], UserService.prototype, \\"getUserList\\", null);
26+
__decorate([
27+
GetExchange(),
28+
ResponseBody(ResponseClass)
29+
], UserService.prototype, \\"getUsersReadonly\\", null);
30+
UserService = __decorate([
31+
HttpInterface()
32+
], UserService);
33+
"
34+
`;
35+
336
exports[`HttpInterfaceVisitor > should ignore if file name is not match 1`] = `
437
"import { HttpInterface, GraphQLExchange } from '@r2don/nest-http-interface';
538
class ResponseClass {

lib/plugin/visitors/http-interface.visitor.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
hasResponseBodyServiceCode,
66
needResponseBodyServiceCode,
77
notPromiseServiceCode,
8+
arrayResponseBodyServiceCode,
89
} from '../../fixtures/fake-service-code';
910
import { before } from '../compiler-plugin';
1011

@@ -103,4 +104,22 @@ describe('HttpInterfaceVisitor', () => {
103104
// then
104105
expect(result.outputText).toMatchSnapshot();
105106
});
107+
108+
test('should handle array return type', () => {
109+
// given
110+
const filename = 'text.service.ts';
111+
const fakeProgram = ts.createProgram([filename], compilerOptions);
112+
113+
// when
114+
const result = ts.transpileModule(arrayResponseBodyServiceCode, {
115+
compilerOptions,
116+
fileName: filename,
117+
transformers: {
118+
before: [before({}, fakeProgram)],
119+
},
120+
});
121+
122+
// then
123+
expect(result.outputText).toMatchSnapshot();
124+
});
106125
});

lib/plugin/visitors/http-interface.visitor.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export class HttpInterfaceVisitor {
107107
return undefined;
108108
}
109109

110-
const innerType = node.type.typeArguments?.[0];
110+
const innerType = this.getInnerType(node.type.typeArguments?.[0]);
111111
if (
112112
innerType == null ||
113113
!ts.isTypeReferenceNode(innerType) ||
@@ -119,6 +119,34 @@ export class HttpInterfaceVisitor {
119119
return innerType.typeName.text;
120120
}
121121

122+
private getInnerType(node?: ts.TypeNode): ts.TypeNode | undefined {
123+
/* c8 ignore next 3 */
124+
if (node == null) {
125+
return undefined;
126+
}
127+
128+
if (ts.isArrayTypeNode(node)) {
129+
return this.getInnerType(node.elementType);
130+
}
131+
132+
if (
133+
ts.isTypeReferenceNode(node) &&
134+
ts.isIdentifier(node.typeName) &&
135+
node.typeName.text === 'Array'
136+
) {
137+
return this.getInnerType(node.typeArguments?.[0]);
138+
}
139+
140+
if (
141+
ts.isTypeOperatorNode(node) &&
142+
node.operator === ts.SyntaxKind.ReadonlyKeyword
143+
) {
144+
return this.getInnerType(node.type);
145+
}
146+
147+
return node;
148+
}
149+
122150
private isExchangeMethod(
123151
member: ts.ClassElement,
124152
): member is ts.MethodDeclaration {

0 commit comments

Comments
 (0)