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 67692c20fc0..da3e7334b6a 100644 --- a/packages/eslint-plugin/src/rules/explicit-function-return-type.ts +++ b/packages/eslint-plugin/src/rules/explicit-function-return-type.ts @@ -9,6 +9,7 @@ type Options = [ allowExpressions?: boolean; allowTypedFunctionExpressions?: boolean; allowHigherOrderFunctions?: boolean; + allowDirectConstAssertionInArrowFunctions?: boolean; }, ]; type MessageIds = 'missingReturnType'; @@ -39,6 +40,9 @@ export default util.createRule({ allowHigherOrderFunctions: { type: 'boolean', }, + allowDirectConstAssertionInArrowFunctions: { + type: 'boolean', + }, }, additionalProperties: false, }, @@ -49,6 +53,7 @@ export default util.createRule({ allowExpressions: false, allowTypedFunctionExpressions: true, allowHigherOrderFunctions: true, + allowDirectConstAssertionInArrowFunctions: true, }, ], create(context, [options]) { @@ -203,6 +208,30 @@ export default util.createRule({ ); } + /** + * Checks if a function belongs to: + * `() => ({ action: 'xxx' }) as const` + */ + function returnsConstAssertionDirectly( + node: TSESTree.ArrowFunctionExpression, + ): boolean { + const { body } = node; + if (body.type === AST_NODE_TYPES.TSAsExpression) { + const { typeAnnotation } = body; + if (typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) { + const { typeName } = typeAnnotation; + if ( + typeName.type === AST_NODE_TYPES.Identifier && + typeName.name === 'const' + ) { + return true; + } + } + } + + return false; + } + /** * Checks if a function declaration/expression has a return type. */ @@ -263,6 +292,15 @@ export default util.createRule({ } } + // https://github.com/typescript-eslint/typescript-eslint/issues/653 + if ( + node.type === AST_NODE_TYPES.ArrowFunctionExpression && + options.allowDirectConstAssertionInArrowFunctions && + returnsConstAssertionDirectly(node) + ) { + return; + } + checkFunctionReturnType(node); } 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 32f23792591..942ed5ea2e8 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 @@ -308,6 +308,20 @@ foo({ }, ], }, + { + filename: 'test.ts', + code: ` +const func = (value: number) => (({ type: "X", value }) as const); +const func = (value: number) => ({ type: "X", value } as const); +const func = (value: number) => (x as const); +const func = (value: number) => x as const; + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: true, + }, + ], + }, ], invalid: [ { @@ -749,5 +763,47 @@ foo({ }, ], }, + { + filename: 'test.ts', + code: ` +const func = (value: number) => ({ type: "X", value } as any); +const func = (value: number) => ({ type: "X", value } as Action); + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: true, + }, + ], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + column: 14, + }, + { + messageId: 'missingReturnType', + line: 3, + column: 14, + }, + ], + }, + { + filename: 'test.ts', + code: ` +const func = (value: number) => ({ type: "X", value } as const); + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: false, + }, + ], + errors: [ + { + messageId: 'missingReturnType', + line: 2, + column: 14, + }, + ], + }, ], });