diff --git a/src/noFunctionExpressionRule.ts b/src/noFunctionExpressionRule.ts index a23158675..0682bafaf 100644 --- a/src/noFunctionExpressionRule.ts +++ b/src/noFunctionExpressionRule.ts @@ -33,27 +33,48 @@ export class Rule extends Lint.Rules.AbstractRule { } class NoFunctionExpressionRuleWalker extends ErrorTolerantWalker { + private allowGenericFunctionExpression: boolean = false; + + constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) { + super(sourceFile, options); + if (sourceFile.fileName.endsWith('tsx')) { + this.allowGenericFunctionExpression = true; + } + } + protected visitFunctionExpression(node: ts.FunctionExpression): void { const walker = new SingleFunctionWalker(this.getSourceFile(), this.getOptions()); node.getChildren().forEach((child: ts.Node) => { walker.walk(child); }); + + const isGenericFunctionInTSX = this.allowGenericFunctionExpression && walker.isGenericFunction; // function expression that access 'this' is allowed - if (!walker.isAccessingThis && !node.asteriskToken) { + if (!walker.isAccessingThis && !node.asteriskToken + // generic function expression in .tsx file is allowed + && !isGenericFunctionInTSX) { this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING); } + super.visitFunctionExpression(node); } } class SingleFunctionWalker extends ErrorTolerantWalker { public isAccessingThis: boolean = false; + public isGenericFunction: boolean = false; protected visitNode(node: ts.Node): void { if (node.getText() === 'this') { this.isAccessingThis = true; } super.visitNode(node); } + + protected visitTypeReference(node: ts.TypeReferenceNode): void { + this.isGenericFunction = true; + super.visitTypeReference(node); + } + /* tslint:disable:no-empty */ protected visitFunctionExpression(): void { // do not visit inner blocks @@ -62,4 +83,4 @@ class SingleFunctionWalker extends ErrorTolerantWalker { // do not visit inner blocks } /* tslint:enable:no-empty */ -} \ No newline at end of file +} diff --git a/src/tests/NoFunctionExpressionRuleTests.ts b/src/tests/NoFunctionExpressionRuleTests.ts index e00850d17..3b1d11566 100644 --- a/src/tests/NoFunctionExpressionRuleTests.ts +++ b/src/tests/NoFunctionExpressionRuleTests.ts @@ -43,6 +43,22 @@ describe('noFunctionExpressionRule', (): void => { TestHelper.assertViolations(ruleName, script, []); }); + it('should fail on not generic type function expression and pass generic type function within a .tsx file', (): void => { + TestHelper.assertViolations( + ruleName, + "test-data/NoFunctionExpressionWithInTSX.tsx", + [{ + "failure": "Use arrow function instead of function expression", + "name": "test-data/NoFunctionExpressionWithInTSX.tsx", + "ruleName": "no-function-expression", + "startPosition": { + "character": 28, + "line": 1 + } + }], + ); + }); + it('should fail on function expression', (): void => { const script: string = ` var x = function() { @@ -94,4 +110,4 @@ describe('noFunctionExpressionRule', (): void => { } ]); }); -}); \ No newline at end of file +}); diff --git a/test-data/NoFunctionExpressionWithInTSX.tsx b/test-data/NoFunctionExpressionWithInTSX.tsx new file mode 100644 index 000000000..985724b12 --- /dev/null +++ b/test-data/NoFunctionExpressionWithInTSX.tsx @@ -0,0 +1,7 @@ +const notGenericFunction = function() { + return 1 + 1; +}; + +const genericFunction = function(x: T): T { + return x; +};