From 9eb150d2ce755f7111ce1350cf6c926ca6de9ea7 Mon Sep 17 00:00:00 2001 From: Klaus Meinhardt Date: Thu, 18 May 2017 22:04:19 +0200 Subject: [PATCH 1/2] strict-type-predicates: warn if strictNullChecks is not enabled --- src/language/utils.ts | 5 + src/rules/strictBooleanExpressionsRule.ts | 2 +- src/rules/strictTypePredicatesRule.ts | 9 +- .../tsconfig.json | 3 +- .../tsconfig.json | 2 +- .../no-strict-null-checks/test.ts.lint | 156 ++++++++++++++++++ .../no-strict-null-checks/tsconfig.json | 5 + .../{ => no-strict-null-checks}/tslint.json | 0 .../{ => strict-null-checks}/test.ts.lint | 0 .../{ => strict-null-checks}/tsconfig.json | 0 .../strict-null-checks/tslint.json | 5 + 11 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 test/rules/strict-type-predicates/no-strict-null-checks/test.ts.lint create mode 100644 test/rules/strict-type-predicates/no-strict-null-checks/tsconfig.json rename test/rules/strict-type-predicates/{ => no-strict-null-checks}/tslint.json (100%) rename test/rules/strict-type-predicates/{ => strict-null-checks}/test.ts.lint (100%) rename test/rules/strict-type-predicates/{ => strict-null-checks}/tsconfig.json (100%) create mode 100644 test/rules/strict-type-predicates/strict-null-checks/tslint.json diff --git a/src/language/utils.ts b/src/language/utils.ts index 4e8fd3fe351..6a80f251ea3 100644 --- a/src/language/utils.ts +++ b/src/language/utils.ts @@ -425,3 +425,8 @@ export function getEqualsKind(node: ts.BinaryOperatorToken): EqualsKind | undefi return undefined; } } + +export function isStrictNullChecksEnabled(options: ts.CompilerOptions): boolean { + return options.strictNullChecks === true || + (options.strict === true && options.strictNullChecks !== false); +} diff --git a/src/rules/strictBooleanExpressionsRule.ts b/src/rules/strictBooleanExpressionsRule.ts index c02e6d6150a..08419091207 100644 --- a/src/rules/strictBooleanExpressionsRule.ts +++ b/src/rules/strictBooleanExpressionsRule.ts @@ -74,7 +74,7 @@ export class Rule extends Lint.Rules.TypedRule { }; public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { - const options = parseOptions(this.ruleArguments, program.getCompilerOptions().strictNullChecks === true); + const options = parseOptions(this.ruleArguments, Lint.isStrictNullChecksEnabled(program.getCompilerOptions())); return this.applyWithFunction(sourceFile, (ctx: Lint.WalkContext) => walk(ctx, program.getTypeChecker()), options); } } diff --git a/src/rules/strictTypePredicatesRule.ts b/src/rules/strictTypePredicatesRule.ts index c32636f4d3d..165cfba3547 100644 --- a/src/rules/strictTypePredicatesRule.ts +++ b/src/rules/strictTypePredicatesRule.ts @@ -18,6 +18,7 @@ import { isBinaryExpression, isUnionType } from "tsutils"; import * as ts from "typescript"; +import { showWarningOnce } from "../error"; import * as Lint from "../index"; // tslint:disable:no-bitwise @@ -31,7 +32,9 @@ export class Rule extends Lint.Rules.TypedRule { Works for 'typeof' comparisons to constants (e.g. 'typeof foo === "string"'), and equality comparison to 'null'/'undefined'. (TypeScript won't let you compare '1 === 2', but it has an exception for '1 === undefined'.) Does not yet work for 'instanceof'. - Does *not* warn for 'if (x.y)' where 'x.y' is always truthy. For that, see strict-boolean-expressions.`, + Does *not* warn for 'if (x.y)' where 'x.y' is always truthy. For that, see strict-boolean-expressions. + + This rule requires \`strictNullChecks\` to work properly.`, optionsDescription: "Not configurable.", options: null, optionExamples: [true], @@ -52,6 +55,10 @@ export class Rule extends Lint.Rules.TypedRule { } public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + if (!Lint.isStrictNullChecksEnabled(program.getCompilerOptions())) { + showWarningOnce("strict-type-predicates does not work without strictNullChecks"); + return []; + } return this.applyWithFunction(sourceFile, (ctx) => walk(ctx, program.getTypeChecker())); } } diff --git a/test/rules/strict-boolean-expressions/no-null-checks-allow-null-union/tsconfig.json b/test/rules/strict-boolean-expressions/no-null-checks-allow-null-union/tsconfig.json index 744a66c893a..32e6931d462 100644 --- a/test/rules/strict-boolean-expressions/no-null-checks-allow-null-union/tsconfig.json +++ b/test/rules/strict-boolean-expressions/no-null-checks-allow-null-union/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { - "module": "commonjs" + "strict": true, + "strictNullChecks": false } } diff --git a/test/rules/strict-boolean-expressions/no-null-checks-allow-number-string/tsconfig.json b/test/rules/strict-boolean-expressions/no-null-checks-allow-number-string/tsconfig.json index 744a66c893a..7cbb28510b8 100644 --- a/test/rules/strict-boolean-expressions/no-null-checks-allow-number-string/tsconfig.json +++ b/test/rules/strict-boolean-expressions/no-null-checks-allow-number-string/tsconfig.json @@ -1,5 +1,5 @@ { "compilerOptions": { - "module": "commonjs" + "strictNullChecks": false } } diff --git a/test/rules/strict-type-predicates/no-strict-null-checks/test.ts.lint b/test/rules/strict-type-predicates/no-strict-null-checks/test.ts.lint new file mode 100644 index 00000000000..bfa10b37067 --- /dev/null +++ b/test/rules/strict-type-predicates/no-strict-null-checks/test.ts.lint @@ -0,0 +1,156 @@ +declare function get(): T; + +// typeof undefined +{ + typeof get() === "undefined"; + + typeof get() === "undefined"; + + typeof get() === "undefined"; + + declare const c: any; + typeof get() === "undefined"; + + // 'undefined' is not assignable to '{}' + typeof get<{}>() === "undefined"; +} + +// typeof boolean +{ + declare const a: boolean; + typeof get() === "boolean"; + + typeof get() === "boolean"; + + typeof get() === "boolean"; + + typeof get<{}>() === "boolean"; +} + +// typeof number +{ + enum E {} + + typeof get() === "number"; + + typeof get() === "number"; +} + +// typeof string +{ + typeof get<"abc" | "def">() === "string"; + + typeof get() === "string"; + + typeof get<"abc" | undefined>() === "string"; +} + +// typeof symbol +{ + typeof get() === "symbol"; + + typeof get() === "symbol"; + + typeof get() === "symbol"; +} + +// typeof function +{ + typeof get<() => void>() === "function"; + + typeof get() === "function"; + + + typeof get() === "function"; + + typeof get void)>() === "function"; + + class X {} + typeof X === "function"; + typeof X === "object"; + + // Works with union + class Foo { } + typeof get === "function"; +} + +// typeof object +{ + typeof get void) | Function>() === "object"; + + typeof get<{}> === "object"; +} + +// === null / undefined +{ + get() === null; + + get() === null; + + get() === undefined; + + get() === undefined; + + // 'null' and 'undefined' are not assignable to '{}' + + get<{}>() === null; + + get<{}>() === undefined; + + get() == null; + + get() == undefined; + + get() == null; + + get() == undefined; + + get() != null; + + get<{}>() == null; + + get() == null; + get() != undefined; + + get() == null; + get() != undefined; +} + +// negation +{ + get() !== null; + + get() !== null; + + get() !== undefined; + + get() !== undefined; + + typeof get() !== "string"; +} + +// reverse left/right +{ + "string" === typeof get(); + + undefined === get(); +} + +// type parameters +{ + function f(t: T) { + typeof t === "boolean"; + } + + // TODO: Would be nice to catch this. + function g(t: T) { + typeof t === "boolean"; + } +} + +// Detects bad typeof +{ + typeof get() === true; + + typeof get() === "orbject"; +} diff --git a/test/rules/strict-type-predicates/no-strict-null-checks/tsconfig.json b/test/rules/strict-type-predicates/no-strict-null-checks/tsconfig.json new file mode 100644 index 00000000000..67394b4f5a1 --- /dev/null +++ b/test/rules/strict-type-predicates/no-strict-null-checks/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + // strictNullChecks not enabled + } +} \ No newline at end of file diff --git a/test/rules/strict-type-predicates/tslint.json b/test/rules/strict-type-predicates/no-strict-null-checks/tslint.json similarity index 100% rename from test/rules/strict-type-predicates/tslint.json rename to test/rules/strict-type-predicates/no-strict-null-checks/tslint.json diff --git a/test/rules/strict-type-predicates/test.ts.lint b/test/rules/strict-type-predicates/strict-null-checks/test.ts.lint similarity index 100% rename from test/rules/strict-type-predicates/test.ts.lint rename to test/rules/strict-type-predicates/strict-null-checks/test.ts.lint diff --git a/test/rules/strict-type-predicates/tsconfig.json b/test/rules/strict-type-predicates/strict-null-checks/tsconfig.json similarity index 100% rename from test/rules/strict-type-predicates/tsconfig.json rename to test/rules/strict-type-predicates/strict-null-checks/tsconfig.json diff --git a/test/rules/strict-type-predicates/strict-null-checks/tslint.json b/test/rules/strict-type-predicates/strict-null-checks/tslint.json new file mode 100644 index 00000000000..bb5f9231e91 --- /dev/null +++ b/test/rules/strict-type-predicates/strict-null-checks/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "strict-type-predicates": true + } +} From 6d2b90745be3b746fc61f759af26571ad3cfa6ab Mon Sep 17 00:00:00 2001 From: Klaus Meinhardt Date: Tue, 30 May 2017 13:40:33 +0200 Subject: [PATCH 2/2] Update warning message --- src/rules/strictTypePredicatesRule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/strictTypePredicatesRule.ts b/src/rules/strictTypePredicatesRule.ts index 165cfba3547..93110a65e43 100644 --- a/src/rules/strictTypePredicatesRule.ts +++ b/src/rules/strictTypePredicatesRule.ts @@ -56,7 +56,7 @@ export class Rule extends Lint.Rules.TypedRule { public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { if (!Lint.isStrictNullChecksEnabled(program.getCompilerOptions())) { - showWarningOnce("strict-type-predicates does not work without strictNullChecks"); + showWarningOnce("strict-type-predicates does not work without --strictNullChecks"); return []; } return this.applyWithFunction(sourceFile, (ctx) => walk(ctx, program.getTypeChecker()));