diff --git a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts index b8e25d4542a..87796f9f427 100644 --- a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts +++ b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts @@ -308,8 +308,16 @@ export default util.createRule({ return; } + // Known edge case: truthy primitives and nullish values are always valid boolean expressions + if ( + (options.allowNumber && is('nullish', 'truthy number')) || + (options.allowString && is('nullish', 'truthy string')) + ) { + return; + } + // string - if (is('string')) { + if (is('string') || is('truthy string')) { if (!options.allowString) { if (isLogicalNegationExpression(node.parent!)) { // if (!string) @@ -458,7 +466,7 @@ export default util.createRule({ } // number - if (is('number')) { + if (is('number') || is('truthy number')) { if (!options.allowNumber) { if (isArrayLengthExpression(node, typeChecker, parserServices)) { if (isLogicalNegationExpression(node.parent!)) { @@ -701,7 +709,9 @@ export default util.createRule({ | 'nullish' | 'boolean' | 'string' + | 'truthy string' | 'number' + | 'truthy number' | 'object' | 'any' | 'never'; @@ -731,21 +741,30 @@ export default util.createRule({ variantTypes.add('boolean'); } - if ( - types.some(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.StringLike)) - ) { - variantTypes.add('string'); + const strings = types.filter(type => + tsutils.isTypeFlagSet(type, ts.TypeFlags.StringLike), + ); + + if (strings.length) { + if (strings.some(type => type.isStringLiteral() && type.value !== '')) { + variantTypes.add('truthy string'); + } else { + variantTypes.add('string'); + } } - if ( - types.some(type => - tsutils.isTypeFlagSet( - type, - ts.TypeFlags.NumberLike | ts.TypeFlags.BigIntLike, - ), - ) - ) { - variantTypes.add('number'); + const numbers = types.filter(type => + tsutils.isTypeFlagSet( + type, + ts.TypeFlags.NumberLike | ts.TypeFlags.BigIntLike, + ), + ); + if (numbers.length) { + if (numbers.some(type => type.isNumberLiteral() && type.value !== 0)) { + variantTypes.add('truthy number'); + } else { + variantTypes.add('number'); + } } if ( diff --git a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts index d24de92ac65..33ec5ba1bad 100644 --- a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts @@ -133,6 +133,57 @@ if (x) { tsconfigRootDir: path.join(rootPath, 'unstrict'), }, }, + + ` +function f(arg: 'a' | null) { + if (arg) console.log(arg); +} + `, + ` +function f(arg: 'a' | 'b' | null) { + if (arg) console.log(arg); +} + `, + { + code: ` +declare const x: 1 | null; +declare const y: 1; +if (x) { +} +if (y) { +} + `, + options: [ + { + allowNumber: true, + }, + ], + }, + ` +function f(arg: 1 | null) { + if (arg) console.log(arg); +} + `, + ` +function f(arg: 1 | 2 | null) { + if (arg) console.log(arg); +} + `, + { + code: ` +declare const x: 'a' | null; +declare const y: 'a'; +if (x) { +} +if (y) { +} + `, + options: [ + { + allowString: true, + }, + ], + }, ], invalid: [