From d66a79e8c731b4079cad4d15ea0aa908b86539bc Mon Sep 17 00:00:00 2001 From: Mark de Dios Date: Wed, 23 Oct 2024 11:33:49 -0700 Subject: [PATCH 01/11] Add unnecessary condition test for falsey big int --- .../tests/rules/no-unnecessary-condition.test.ts | 4 ++++ 1 file changed, 4 insertions(+) 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 629228f4acc7..9d42438eea6f 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -1040,6 +1040,10 @@ switch (b1) { unnecessaryConditionTest('void', 'alwaysFalsy'), unnecessaryConditionTest('never', 'never'), unnecessaryConditionTest('string & number', 'never'), + unnecessaryConditionTest( + 'declare const falseyBigInt: 0n; if (falseyBigInt) {}', + 'alwaysFalsy', + ), // More complex logical expressions { From 159c5fb5da1e96ba7fe75b6b40e7746ccbe0e776 Mon Sep 17 00:00:00 2001 From: Mark de Dios Date: Thu, 24 Oct 2024 12:09:20 -0700 Subject: [PATCH 02/11] Add function to get big int from pseudo big int --- .../src/rules/no-unnecessary-condition.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index ed963aa4703b..a31ad2dc153e 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -24,12 +24,25 @@ import { findTypeGuardAssertedArgument, } from '../util/assertionFunctionUtils'; +const valueIsPseudoBigInt = ( + value: number | string | ts.PseudoBigInt, +): value is ts.PseudoBigInt => { + return typeof value === 'object' && 'base10Value' in value; +}; + +const getValue = (type: ts.LiteralType): bigint | number | string => { + if (valueIsPseudoBigInt(type.value)) { + return BigInt(type.value.base10Value); + } + return type.value; +}; + // Truthiness utilities // #region const isTruthyLiteral = (type: ts.Type): boolean => tsutils.isTrueLiteralType(type) || // || type. - (type.isLiteral() && !!type.value); + (type.isLiteral() && !!getValue(type)); const isPossiblyFalsy = (type: ts.Type): boolean => tsutils From e9e683070414e760d56bc12cd3b95a042c12b93c Mon Sep 17 00:00:00 2001 From: Mark de Dios Date: Thu, 24 Oct 2024 13:32:27 -0700 Subject: [PATCH 03/11] Remove semicolons from getValue function in no unnecessary condition, change test format for falsey big int --- .../tests/rules/no-unnecessary-condition.test.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 9d42438eea6f..c9001a6fd964 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -1040,14 +1040,17 @@ switch (b1) { unnecessaryConditionTest('void', 'alwaysFalsy'), unnecessaryConditionTest('never', 'never'), unnecessaryConditionTest('string & number', 'never'), - unnecessaryConditionTest( - 'declare const falseyBigInt: 0n; if (falseyBigInt) {}', - 'alwaysFalsy', - ), - // More complex logical expressions { code: ` +declare const falseyBigInt: 0n; +if (falseyBigInt) { +} + `, + errors: [ruleError(2, 5, 'alwaysFalsy')], + }, + { + code: ` declare const b1: boolean; declare const b2: boolean; if (true && b1 && b2) { From c5e57eea6dd481a82c6e6c56bd05cc0ff3077e3d Mon Sep 17 00:00:00 2001 From: Mark de Dios Date: Thu, 24 Oct 2024 15:21:24 -0700 Subject: [PATCH 04/11] Add utility function to check if type is falsey big int --- .../src/rules/no-unnecessary-condition.ts | 13 +++++++++++-- .../tests/rules/no-unnecessary-condition.test.ts | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index a31ad2dc153e..3a0e19cc4e95 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -24,6 +24,8 @@ import { findTypeGuardAssertedArgument, } from '../util/assertionFunctionUtils'; +// Truthiness utilities +// #region const valueIsPseudoBigInt = ( value: number | string | ts.PseudoBigInt, ): value is ts.PseudoBigInt => { @@ -37,8 +39,13 @@ const getValue = (type: ts.LiteralType): bigint | number | string => { return type.value; }; -// Truthiness utilities -// #region +const isFalseyBigInt = (type: ts.Type): boolean => { + return ( + tsutils.isLiteralType(type) && + valueIsPseudoBigInt(type.value) && + !getValue(type) + ); +}; const isTruthyLiteral = (type: ts.Type): boolean => tsutils.isTrueLiteralType(type) || // || type. @@ -319,6 +326,8 @@ export default createRule({ messageId = !isUnaryNotArgument ? 'alwaysFalsy' : 'alwaysTruthy'; } else if (!isPossiblyFalsy(type)) { messageId = !isUnaryNotArgument ? 'alwaysTruthy' : 'alwaysFalsy'; + } else if (isFalseyBigInt(type)) { + messageId = 'alwaysFalsy'; } if (messageId) { 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 c9001a6fd964..90f7e40595b3 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -1047,7 +1047,7 @@ declare const falseyBigInt: 0n; if (falseyBigInt) { } `, - errors: [ruleError(2, 5, 'alwaysFalsy')], + errors: [ruleError(3, 5, 'alwaysFalsy')], }, { code: ` From 4a858e6f4a27a6c2be56f9bfbf1e8788cef84d76 Mon Sep 17 00:00:00 2001 From: Mark de Dios <99303358+peanutenthusiast@users.noreply.github.com> Date: Sun, 27 Oct 2024 14:13:49 -0700 Subject: [PATCH 05/11] Remove check for base10Value in valueIsPseudoBigInt Co-authored-by: Kirk Waiblinger --- packages/eslint-plugin/src/rules/no-unnecessary-condition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 3a0e19cc4e95..a5a7d14c22e6 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -29,7 +29,7 @@ import { const valueIsPseudoBigInt = ( value: number | string | ts.PseudoBigInt, ): value is ts.PseudoBigInt => { - return typeof value === 'object' && 'base10Value' in value; + return typeof value === 'object' }; const getValue = (type: ts.LiteralType): bigint | number | string => { From 986e138b3888e450fa90425f36f0a2b1cb807a48 Mon Sep 17 00:00:00 2001 From: Mark de Dios Date: Sun, 27 Oct 2024 14:59:26 -0700 Subject: [PATCH 06/11] Add isFalseyBigInt invocation to isPossibly truthy for ts api utils work around --- .../src/rules/no-unnecessary-condition.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index a5a7d14c22e6..66a9e92653a0 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -29,7 +29,7 @@ import { const valueIsPseudoBigInt = ( value: number | string | ts.PseudoBigInt, ): value is ts.PseudoBigInt => { - return typeof value === 'object' + return typeof value === 'object'; }; const getValue = (type: ts.LiteralType): bigint | number | string => { @@ -69,7 +69,13 @@ const isPossiblyTruthy = (type: ts.Type): boolean => .some(intersectionParts => // It is possible to define intersections that are always falsy, // like `"" & { __brand: string }`. - intersectionParts.every(type => !tsutils.isFalsyType(type)), + intersectionParts.every( + type => + !tsutils.isFalsyType(type) && + // below is a workaround for ts-api-utils bug + // see https://github.com/JoshuaKGoldberg/ts-api-utils/issues/544 + !isFalseyBigInt(type), + ), ); // Nullish utilities @@ -326,8 +332,6 @@ export default createRule({ messageId = !isUnaryNotArgument ? 'alwaysFalsy' : 'alwaysTruthy'; } else if (!isPossiblyFalsy(type)) { messageId = !isUnaryNotArgument ? 'alwaysTruthy' : 'alwaysFalsy'; - } else if (isFalseyBigInt(type)) { - messageId = 'alwaysFalsy'; } if (messageId) { From ac06300411d0a9b28cdff23a50ed771038059494 Mon Sep 17 00:00:00 2001 From: Mark de Dios Date: Wed, 30 Oct 2024 12:28:43 -0700 Subject: [PATCH 07/11] Add test cases for positive and negative bigint values --- .../tests/rules/no-unnecessary-condition.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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 90f7e40595b3..a9e31d84b4b6 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -1051,6 +1051,22 @@ if (falseyBigInt) { }, { code: ` +declare const posbigInt: 1n; +if (posbigInt) { +} + `, + errors: [ruleError(3, 5, 'alwaysTruthy')], + }, + { + code: ` +declare const negBigInt: -2n; +if (negBigInt) { +} + `, + errors: [ruleError(3, 5, 'alwaysTruthy')], + }, + { + code: ` declare const b1: boolean; declare const b2: boolean; if (true && b1 && b2) { From ee0544f70ff73491751e0066d516c5130eb5259f Mon Sep 17 00:00:00 2001 From: Mark de Dios <99303358+peanutenthusiast@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:47:29 -0700 Subject: [PATCH 08/11] Update packages/eslint-plugin/src/rules/no-unnecessary-condition.ts Co-authored-by: Kirk Waiblinger --- packages/eslint-plugin/src/rules/no-unnecessary-condition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 66a9e92653a0..0ab1b74ca4fc 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -34,7 +34,7 @@ const valueIsPseudoBigInt = ( const getValue = (type: ts.LiteralType): bigint | number | string => { if (valueIsPseudoBigInt(type.value)) { - return BigInt(type.value.base10Value); + return BigInt((type.value.negative ? '-' : '') + type.value.base10Value); } return type.value; }; From 4819d6417f79d97608e0fcffb959518b58a2de06 Mon Sep 17 00:00:00 2001 From: Mark de Dios Date: Wed, 30 Oct 2024 17:15:39 -0700 Subject: [PATCH 09/11] Add valid test case for bigint: 0n | 1n --- .../tests/rules/no-unnecessary-condition.test.ts | 5 +++++ 1 file changed, 5 insertions(+) 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 a9e31d84b4b6..d3042406ed1d 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -85,6 +85,11 @@ switch (b1) { declare function foo(): number | void; const result1 = foo() === undefined; const result2 = foo() == null; + `, + ` +declare const bigInt: 0n | 1n; +if (bigInt) { +} `, necessaryConditionTest('false | 5'), // Truthy literal and falsy literal necessaryConditionTest('boolean | "foo"'), // boolean and truthy literal From d9166278dc934147a50d1858a50212e5bc2ecac1 Mon Sep 17 00:00:00 2001 From: Mark de Dios <99303358+peanutenthusiast@users.noreply.github.com> Date: Thu, 31 Oct 2024 08:16:00 -0700 Subject: [PATCH 10/11] Update packages/eslint-plugin/src/rules/no-unnecessary-condition.ts Co-authored-by: Joshua Chen --- packages/eslint-plugin/src/rules/no-unnecessary-condition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 0ab1b74ca4fc..4155e232a5a8 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -39,7 +39,7 @@ const getValue = (type: ts.LiteralType): bigint | number | string => { return type.value; }; -const isFalseyBigInt = (type: ts.Type): boolean => { +const isFalsyBigInt = (type: ts.Type): boolean => { return ( tsutils.isLiteralType(type) && valueIsPseudoBigInt(type.value) && From 3cc6ae68edc28953d8e317a451a2854c0fab8fd2 Mon Sep 17 00:00:00 2001 From: Mark de Dios Date: Thu, 31 Oct 2024 08:18:15 -0700 Subject: [PATCH 11/11] Fix isFalsyBigInt invocation --- packages/eslint-plugin/src/rules/no-unnecessary-condition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 4155e232a5a8..692dc2e9e400 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -74,7 +74,7 @@ const isPossiblyTruthy = (type: ts.Type): boolean => !tsutils.isFalsyType(type) && // below is a workaround for ts-api-utils bug // see https://github.com/JoshuaKGoldberg/ts-api-utils/issues/544 - !isFalseyBigInt(type), + !isFalsyBigInt(type), ), );