diff --git a/packages/cli/src/utils/jsDocUtils.ts b/packages/cli/src/utils/jsDocUtils.ts index dc85658ad..51a8c2fd5 100644 --- a/packages/cli/src/utils/jsDocUtils.ts +++ b/packages/cli/src/utils/jsDocUtils.ts @@ -39,7 +39,7 @@ export function getJSDocTagNames(node: ts.Node, requireTagName = false) { tags = getJSDocTags(node.parent as any, tag => { if (ts.isJSDocParameterTag(tag)) { return false; - } else if (tag.comment === undefined) { + } else if (tag.comment === undefined && !ts.isJSDocReturnTag(tag)) { throw new GenerateMetadataError(`Orphan tag: @${String(tag.tagName.text || tag.tagName.escapedText)} should have a parameter name follows with.`); } diff --git a/tests/fixtures/controllers/controllerWithJsDocResponseDescription.ts b/tests/fixtures/controllers/controllerWithJsDocResponseDescription.ts index 6c74e0aad..4e9d6c658 100644 --- a/tests/fixtures/controllers/controllerWithJsDocResponseDescription.ts +++ b/tests/fixtures/controllers/controllerWithJsDocResponseDescription.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Route, SuccessResponse } from '@tsoa/runtime'; +import { Body, Controller, Get, Post, Route, SuccessResponse } from '@tsoa/runtime'; import { TestModel } from '../testModel'; import { ModelService } from 'fixtures/services/modelService'; @@ -16,6 +16,18 @@ export class CustomResponseDescController extends Controller { return new ModelService().getModel(); } + /** @returns */ + @Post('emptyReturnsTag') + public async emptyReturnsTag(@Body() body: TestModel): Promise { + return body.id; + } + + /** @returns {Promise} */ + @Post('typeOnlyReturnsTag') + public async typeOnlyReturnsTag(@Body() body: TestModel): Promise { + return body.id; + } + @Get('successResponseAndJsDocAnnotation') /** * @returns custom description from jsdoc annotation diff --git a/tests/unit/swagger/definitionsGeneration/metadata.spec.ts b/tests/unit/swagger/definitionsGeneration/metadata.spec.ts index 48774247b..9b9732d61 100644 --- a/tests/unit/swagger/definitionsGeneration/metadata.spec.ts +++ b/tests/unit/swagger/definitionsGeneration/metadata.spec.ts @@ -1042,6 +1042,17 @@ describe('Metadata generation', () => { expect(method.responses[0].name).to.equal('200'); expect(method.responses[0].description).to.equal(description); }); + + it('ignores empty and type-only @returns tags', () => { + for (const methodName of ['emptyReturnsTag', 'typeOnlyReturnsTag']) { + const method = controller.methods.find(m => m.name === methodName); + if (!method) { + throw new Error(`method ${methodName} not defined`); + } + expect(method.responses[0].description).to.equal('Ok'); + } + }); + it("should not override @SuccessResponse's description even if @returns is present", () => { const description = 'Success Response description'; const method = controller.methods.find(m => m.name === 'successResponseAndJsDocAnnotation');