From 891693c3b005d20c5be8374822134a8b27189496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnaud=20Barr=C3=A9?= Date: Tue, 17 Nov 2020 01:48:17 +0100 Subject: [PATCH 1/2] fix(eslint-plugin): false positive alwaysTruthyFunc for no-unnecessary-condition when predicate returns unknown --- .../eslint-plugin/src/rules/no-unnecessary-condition.ts | 2 ++ .../tests/rules/no-unnecessary-condition.test.ts | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 3e1ad26bcda..acfa2e1da3d 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -33,6 +33,8 @@ const isTruthyLiteral = (type: ts.Type): boolean => isBooleanLiteralType(type, true) || (isLiteralType(type) && !!type.value); const isPossiblyFalsy = (type: ts.Type): boolean => + isTypeUnknownType(type) || + isTypeAnyType(type) || unionTypeParts(type) // PossiblyFalsy flag includes literal values, so exclude ones that // are definitely truthy diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts index 2afba392673..2658061a357 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -235,6 +235,14 @@ function length(x: string) { function nonEmptyStrings(x: string[]) { return x.filter(length); } + +// filter-like predicate +function count( + list: string[], + predicate: (value: string, index: number, array: string[]) => unknown, +) { + return list.filter(predicate).length; +} `, // Ignores non-array methods of the same name ` From 2a4e958cabc84fb7ef11342291b2c1d0e09b6817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnaud=20Barr=C3=A9?= Date: Tue, 17 Nov 2020 02:46:26 +0100 Subject: [PATCH 2/2] Local check --- .../eslint-plugin/src/rules/no-unnecessary-condition.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index acfa2e1da3d..aee4963a0c0 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -33,8 +33,6 @@ const isTruthyLiteral = (type: ts.Type): boolean => isBooleanLiteralType(type, true) || (isLiteralType(type) && !!type.value); const isPossiblyFalsy = (type: ts.Type): boolean => - isTypeUnknownType(type) || - isTypeAnyType(type) || unionTypeParts(type) // PossiblyFalsy flag includes literal values, so exclude ones that // are definitely truthy @@ -452,6 +450,10 @@ export default createRule({ // Not a callable function return; } + // Predicate is always necessary if it involves `any` or `unknown` + if (returnTypes.some(t => isTypeAnyType(t) || isTypeUnknownType(t))) { + return; + } if (!returnTypes.some(isPossiblyFalsy)) { return context.report({ node: callback,