From 4da9278c80706f420d4c15a71c11c7b11d935643 Mon Sep 17 00:00:00 2001 From: Taeheon Kim Date: Sun, 13 Feb 2022 22:47:41 +0900 Subject: [PATCH] fix: support nested object deconstructuring with type annotation (#4548) --- packages/eslint-plugin/src/rules/typedef.ts | 25 +++++++- .../eslint-plugin/tests/rules/typedef.test.ts | 58 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/typedef.ts b/packages/eslint-plugin/src/rules/typedef.ts index ed78693d48b..89ef30f54e5 100644 --- a/packages/eslint-plugin/src/rules/typedef.ts +++ b/packages/eslint-plugin/src/rules/typedef.ts @@ -149,6 +149,25 @@ export default util.createRule<[Options], MessageIds>({ ); } + function isAncestorHasTypeAnnotation( + node: TSESTree.ObjectPattern, + ): boolean { + let ancestor = node.parent; + + while (ancestor) { + if ( + ancestor.type === AST_NODE_TYPES.ObjectPattern && + ancestor.typeAnnotation + ) { + return true; + } + + ancestor = ancestor.parent; + } + + return false; + } + return { ...(arrayDestructuring && { ArrayPattern(node): void { @@ -193,7 +212,11 @@ export default util.createRule<[Options], MessageIds>({ }), ...(objectDestructuring && { ObjectPattern(node): void { - if (!node.typeAnnotation && !isForOfStatementContext(node)) { + if ( + !node.typeAnnotation && + !isForOfStatementContext(node) && + !isAncestorHasTypeAnnotation(node) + ) { report(node); } }, diff --git a/packages/eslint-plugin/tests/rules/typedef.test.ts b/packages/eslint-plugin/tests/rules/typedef.test.ts index e237ff68636..d1bae842614 100644 --- a/packages/eslint-plugin/tests/rules/typedef.test.ts +++ b/packages/eslint-plugin/tests/rules/typedef.test.ts @@ -201,6 +201,26 @@ ruleTester.run('typedef', rule, { }, ], }, + { + code: ` + const { + id, + details: { + name: { + first, + middle, + last, + forTest: { moreNested }, + }, + }, + }: User = getUser(); + `, + options: [ + { + objectDestructuring: true, + }, + ], + }, // Function parameters 'function receivesNumber(a: number): void {}', 'function receivesStrings(a: string, b: string): void {}', @@ -516,6 +536,44 @@ class ClassName { }, ], }, + { + code: ` + const { + id, + details: { + name: { + first, + middle, + last, + forTest: { moreNested }, + }, + }, + } = getUser(); + `, + errors: [ + { + data: { name: 'first' }, + messageId: 'expectedTypedef', + }, + { + data: { name: 'middle' }, + messageId: 'expectedTypedef', + }, + { + data: { name: 'last' }, + messageId: 'expectedTypedef', + }, + { + data: { name: 'moreNested' }, + messageId: 'expectedTypedef', + }, + ], + options: [ + { + objectDestructuring: true, + }, + ], + }, // Arrow parameters { code: 'const receivesNumber = (a): void => {};',