diff --git a/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts b/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts index fd843b83a0e9..e498d373a3e4 100644 --- a/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts +++ b/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts @@ -176,15 +176,12 @@ function doesImmediatelyReturnFunctionExpression({ /** * Checks if a function belongs to: * ``` - * () => ({ action: 'xxx' } as const) + * ({ action: 'xxx' } as const) * ``` */ -function returnsConstAssertionDirectly( - node: TSESTree.ArrowFunctionExpression, -): boolean { - const { body } = node; - if (isTypeAssertion(body)) { - const { typeAnnotation } = body; +function isConstAssertion(node: TSESTree.Node): boolean { + if (isTypeAssertion(node)) { + const { typeAnnotation } = node; if (typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) { const { typeName } = typeAnnotation; if ( @@ -256,11 +253,19 @@ function isValidFunctionExpressionReturnType( } // https://github.com/typescript-eslint/typescript-eslint/issues/653 - return ( - options.allowDirectConstAssertionInArrowFunctions === true && - node.type === AST_NODE_TYPES.ArrowFunctionExpression && - returnsConstAssertionDirectly(node) - ); + if ( + !options.allowDirectConstAssertionInArrowFunctions || + node.type !== AST_NODE_TYPES.ArrowFunctionExpression + ) { + return false; + } + + let body = node.body; + while (body.type === AST_NODE_TYPES.TSSatisfiesExpression) { + body = body.expression; + } + + return isConstAssertion(body); } /** 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 52eafebb8e04..e02e701da7de 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 @@ -443,6 +443,53 @@ const func = (value: number) => x as const; }, { code: ` +interface R { + type: string; + value: number; +} + +const func = (value: number) => ({ type: 'X', value }) as const satisfies R; + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: true, + }, + ], + }, + { + code: ` +interface R { + type: string; + value: number; +} + +const func = (value: number) => + ({ type: 'X', value }) as const satisfies R satisfies R; + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: true, + }, + ], + }, + { + code: ` +interface R { + type: string; + value: number; +} + +const func = (value: number) => + ({ type: 'X', value }) as const satisfies R satisfies R satisfies R; + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: true, + }, + ], + }, + { + code: ` new Promise(resolve => {}); new Foo(1, () => {}); `, @@ -1731,6 +1778,30 @@ const func = (value: number) => ({ type: 'X', value }) as const; }, ], }, + { + code: ` +interface R { + type: string; + value: number; +} + +const func = (value: number) => ({ type: 'X', value }) as const satisfies R; + `, + errors: [ + { + column: 30, + endColumn: 32, + endLine: 7, + line: 7, + messageId: 'missingReturnType', + }, + ], + options: [ + { + allowDirectConstAssertionInArrowFunctions: false, + }, + ], + }, { code: 'const log = (message: string) => void console.log(message);', errors: [ diff --git a/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts b/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts index d68e21b70b69..45deb7d1124f 100644 --- a/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts +++ b/packages/eslint-plugin/tests/rules/explicit-module-boundary-types.test.ts @@ -367,6 +367,54 @@ export const func4 = (value: number) => x as const; }, { code: ` +interface R { + type: string; + value: number; +} + +export const func = (value: number) => + ({ type: 'X', value }) as const satisfies R; + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: true, + }, + ], + }, + { + code: ` +interface R { + type: string; + value: number; +} + +export const func = (value: number) => + ({ type: 'X', value }) as const satisfies R satisfies R; + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: true, + }, + ], + }, + { + code: ` +interface R { + type: string; + value: number; +} + +export const func = (value: number) => + ({ type: 'X', value }) as const satisfies R satisfies R satisfies R; + `, + options: [ + { + allowDirectConstAssertionInArrowFunctions: true, + }, + ], + }, + { + code: ` export const func1 = (value: string) => value; export const func2 = (value: number) => ({ type: 'X', value }); `, @@ -1204,6 +1252,31 @@ export const func = (value: number) => ({ type: 'X', value }) as const; }, { code: ` +interface R { + type: string; + value: number; +} + +export const func = (value: number) => + ({ type: 'X', value }) as const satisfies R; + `, + errors: [ + { + column: 37, + endColumn: 39, + endLine: 7, + line: 7, + messageId: 'missingReturnType', + }, + ], + options: [ + { + allowDirectConstAssertionInArrowFunctions: false, + }, + ], + }, + { + code: ` export class Test { constructor() {} get prop() {