From 6a0d3b62149037a29153386289e3eb7bca7fc059 Mon Sep 17 00:00:00 2001 From: Guy Perkal Date: Sun, 29 Sep 2019 21:05:07 +0200 Subject: [PATCH] feat(eslint-plugin): [explicit-function-return-type] add ignoreUnexportedFunctions option --- .../rules/explicit-function-return-type.md | 24 +++ .../rules/explicit-function-return-type.ts | 25 +++ .../explicit-function-return-type.test.ts | 170 ++++++++++++++++++ 3 files changed, 219 insertions(+) diff --git a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md index 7e3093ebfaa0..f12cb8a4327d 100644 --- a/packages/eslint-plugin/docs/rules/explicit-function-return-type.md +++ b/packages/eslint-plugin/docs/rules/explicit-function-return-type.md @@ -69,6 +69,8 @@ type Options = { allowTypedFunctionExpressions?: boolean; // if true, functions immediately returning another function expression will not be checked allowHigherOrderFunctions?: boolean; + // if true, functions not directly exported will not be checked + ignoreUnexportedFunctions?: boolean; }; const defaults = { @@ -198,6 +200,28 @@ function fn() { } ``` +### ignoreUnexportedFunctions + +Examples of **incorrect** code for this rule with `{ ignoreUnexportedFunctions: true }`: + +```ts +export default () => () => {}; + +export const arrowFn = () => {}; + +export function fn() { + return function() {}; +} +``` + +Examples of **correct** code for this rule with `{ ignoreUnexportedFunctions: true }`: + +```ts +function viaDoubleVariableReference() {} +const variableRefOne = viaDoubleVariableReference; +export const variableRefTwo = variableRefOne; +``` + ## When Not To Use It If you don't wish to prevent calling code from using function return values in unexpected ways, then diff --git a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts index 43a1cac41fb4..7e9e35b6827c 100644 --- a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts +++ b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts @@ -11,6 +11,7 @@ type Options = [ allowTypedFunctionExpressions?: boolean; allowHigherOrderFunctions?: boolean; allowDirectConstAssertionInArrowFunctions?: boolean; + ignoreUnexportedFunctions?: boolean; }, ]; type MessageIds = 'missingReturnType'; @@ -44,6 +45,9 @@ export default util.createRule({ allowDirectConstAssertionInArrowFunctions: { type: 'boolean', }, + ignoreUnexportedFunctions: { + type: 'boolean', + }, }, additionalProperties: false, }, @@ -55,6 +59,7 @@ export default util.createRule({ allowTypedFunctionExpressions: true, allowHigherOrderFunctions: true, allowDirectConstAssertionInArrowFunctions: true, + ignoreUnexportedFunctions: false, }, ], create(context, [options]) { @@ -197,6 +202,22 @@ export default util.createRule({ ); } + function isUnexported(node: TSESTree.Node | undefined): boolean { + while (node) { + if ( + node.type === AST_NODE_TYPES.ExportDefaultDeclaration || + node.type === AST_NODE_TYPES.ExportNamedDeclaration || + node.type === AST_NODE_TYPES.ExportSpecifier + ) { + return false; + } + + node = node.parent; + } + + return true; + } + /** * Checks if a function belongs to: * `() => () => ...` @@ -296,6 +317,10 @@ export default util.createRule({ return; } + if (options.ignoreUnexportedFunctions && isUnexported(node.parent)) { + return; + } + if ( node.returnType || isConstructor(node.parent) || diff --git a/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts index bd69534089db..57ff60188ce1 100644 --- a/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts @@ -322,6 +322,82 @@ const func = (value: number) => x as const; }, ], }, + { + filename: 'test.ts', + code: ` +function test( + a: number, + b: number, +) { + return; +} + `, + options: [ + { + ignoreUnexportedFunctions: true, + }, + ], + }, + { + filename: 'test.ts', + code: ` +export var arrowFn = (): string => 'test'; +var fn = function() { + return 1; +}; + `, + options: [ + { + ignoreUnexportedFunctions: true, + }, + ], + }, + { + filename: 'test.ts', + code: ` +class Test { + constructor() {} + get prop() { + return 1; + } + set prop() {} + method() { + return; + } + arrow = () => 'arrow'; + private method() { + return; + } +} + `, + options: [ + { + ignoreUnexportedFunctions: true, + }, + ], + }, + { + filename: 'test.ts', + code: ` +function viaDoubleVariableReference() {} +const variableRefOne = viaDoubleVariableReference; +export const variableRefTwo = variableRefOne; + `, + options: [ + { + ignoreUnexportedFunctions: true, + }, + ], + }, + { + filename: 'test.ts', + code: `export default (): void => {}`, + options: [ + { + ignoreUnexportedFunctions: true, + }, + ], + }, ], invalid: [ { @@ -968,5 +1044,99 @@ const func = (value: number) => ({ type: "X", value } as const); }, ], }, + { + filename: 'test.ts', + code: ` +export function test( + a: number, + b: number, +) { + return; +} + `, + options: [ + { + ignoreUnexportedFunctions: true, + }, + ], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + endLine: 5, + column: 8, + endColumn: 2, + }, + ], + }, + { + filename: 'test.ts', + code: ` +export var fn = function() { + return 1; +}; + `, + options: [ + { + ignoreUnexportedFunctions: false, + }, + ], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + endLine: 2, + column: 17, + endColumn: 27, + }, + ], + }, + { + filename: 'test.ts', + code: ` +export class Test { + constructor() {} + get prop() { + return 1; + } + set prop() {} + method() { + return; + } + arrow = (): string => 'arrow'; + private method() { + return; + } +} + `, + options: [ + { + ignoreUnexportedFunctions: false, + }, + ], + errors: [ + { + messageId: 'missingReturnType', + line: 4, + endLine: 4, + column: 3, + endColumn: 13, + }, + { + messageId: 'missingReturnType', + line: 8, + endLine: 8, + column: 3, + endColumn: 11, + }, + { + messageId: 'missingReturnType', + line: 12, + endLine: 12, + column: 3, + endColumn: 19, + }, + ], + }, ], });