From 36ed6a404a6a93363f15fca2c4f73fb0975ba1fb Mon Sep 17 00:00:00 2001 From: phaux Date: Sat, 3 Aug 2019 00:08:18 +0200 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Add=20allowNullable?= =?UTF-8?q?=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add allowNullable option to strict-boolean-expressions rule --- .../docs/rules/strict-boolean-expressions.md | 1 + .../src/rules/strict-boolean-expressions.ts | 36 ++++++++++++++++--- .../rules/strict-boolean-expressions.test.ts | 22 ++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md b/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md index de27437a47c..5b6aedef057 100644 --- a/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md +++ b/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md @@ -56,6 +56,7 @@ while (typeof str !== 'undefined') { Options may be provided as an object with: +- `allowNullable` to allow `undefined` and `null` in addition to `boolean` as a type of all boolean expressions. (`false` by default). - `ignoreRhs` to skip the check on the right hand side of expressions like `a && b` or `a || b` - allows these operators to be used for their short-circuiting behavior. (`false` by default). ## Related To diff --git a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts index d9077bf3154..c29dc28bde9 100644 --- a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts +++ b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts @@ -16,7 +16,8 @@ type ExpressionWithTest = type Options = [ { ignoreRhs?: boolean; - }, + allowNullable?: boolean; + } ]; export default util.createRule({ @@ -35,6 +36,9 @@ export default util.createRule({ ignoreRhs: { type: 'boolean', }, + allowNullable: { + type: 'boolean', + }, }, additionalProperties: false, }, @@ -46,9 +50,10 @@ export default util.createRule({ defaultOptions: [ { ignoreRhs: false, + allowNullable: false, }, ], - create(context, [{ ignoreRhs }]) { + create(context, [options]) { const service = util.getParserServices(context); const checker = service.program.getTypeChecker(); @@ -60,7 +65,30 @@ export default util.createRule({ node, ); const type = util.getConstrainedTypeAtLocation(checker, tsNode); - return tsutils.isTypeFlagSet(type, ts.TypeFlags.BooleanLike); + + if (tsutils.isTypeFlagSet(type, ts.TypeFlags.BooleanLike)) { + return true; + } + + // Check variants of union + if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Union)) { + let hasBoolean = false; + for (const ty of (type as ts.UnionType).types) { + if (tsutils.isTypeFlagSet(ty, ts.TypeFlags.BooleanLike)) { + hasBoolean = true; + continue; + } + if (options.allowNullable) { + if (tsutils.isTypeFlagSet(ty, ts.TypeFlags.Null)) continue; + if (tsutils.isTypeFlagSet(ty, ts.TypeFlags.Undefined)) continue; + } + // Union variant is something else + return false; + } + return hasBoolean; + } + + return false; } /** @@ -87,7 +115,7 @@ export default util.createRule({ ): void { if ( !isBooleanType(node.left) || - (!ignoreRhs && !isBooleanType(node.right)) + (!options.ignoreRhs && !isBooleanType(node.right)) ) { reportNode(node); } 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 1067b5fa4c6..300b6d803a7 100644 --- a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts @@ -165,6 +165,15 @@ const boolOrObj = bool || obj; const boolAndObj = bool && obj; `, }, + { + options: [{ allowNullable: true }], + code: ` + const f1 = (x?: boolean) => x ? 1 : 0; + const f2 = (x: boolean | null) => x ? 1 : 0; + const f3 = (x?: true | null) => x ? 1 : 0; + const f4 = (x?: false) => x ? 1 : 0; + `, + }, ], invalid: [ @@ -933,5 +942,18 @@ const objOrBool = obj || bool; const objAndBool = obj && bool; `, }, + { + options: [{ allowNullable: true }], + errors: [ + { + messageId: 'strictBooleanExpression', + line: 2, + column: 44, + }, + ], + code: ` + const f = (x: null | undefined) => x ? 1 : 0; + `, + }, ], }); From 37b9c3b75ccb675e226dfe233e40f76747d611c7 Mon Sep 17 00:00:00 2001 From: phaux Date: Sat, 3 Aug 2019 20:25:52 +0200 Subject: [PATCH 2/3] =?UTF-8?q?style:=20=F0=9F=92=84=20Fix=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/rules/strict-boolean-expressions.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts index c29dc28bde9..23943ce109f 100644 --- a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts +++ b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts @@ -17,7 +17,7 @@ type Options = [ { ignoreRhs?: boolean; allowNullable?: boolean; - } + }, ]; export default util.createRule({ @@ -79,8 +79,12 @@ export default util.createRule({ continue; } if (options.allowNullable) { - if (tsutils.isTypeFlagSet(ty, ts.TypeFlags.Null)) continue; - if (tsutils.isTypeFlagSet(ty, ts.TypeFlags.Undefined)) continue; + if (tsutils.isTypeFlagSet(ty, ts.TypeFlags.Null)) { + continue; + } + if (tsutils.isTypeFlagSet(ty, ts.TypeFlags.Undefined)) { + continue; + } } // Union variant is something else return false; From cfba7bd71731c57dea07b192ba38a56940f39717 Mon Sep 17 00:00:00 2001 From: phaux Date: Tue, 13 Aug 2019 21:19:31 +0200 Subject: [PATCH 3/3] =?UTF-8?q?test:=20=F0=9F=92=8D=20Add=20missing=20case?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/rules/strict-boolean-expressions.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) 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 300b6d803a7..c95228b6743 100644 --- a/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts +++ b/packages/eslint-plugin/tests/rules/strict-boolean-expressions.test.ts @@ -950,9 +950,15 @@ const objAndBool = obj && bool; line: 2, column: 44, }, + { + messageId: 'strictBooleanExpression', + line: 3, + column: 35, + }, ], code: ` const f = (x: null | undefined) => x ? 1 : 0; + const f = (x?: number) => x ? 1 : 0; `, }, ],