Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

strict-type-predicates: warn if strictNullChecks is not enabled #2786

Merged
merged 4 commits into from May 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/language/utils.ts
Expand Up @@ -426,6 +426,11 @@ export function getEqualsKind(node: ts.BinaryOperatorToken): EqualsKind | undefi
}
}

export function isStrictNullChecksEnabled(options: ts.CompilerOptions): boolean {
return options.strictNullChecks === true ||
(options.strict === true && options.strictNullChecks !== false);
}

export function isNegativeNumberLiteral(node: ts.Node): node is ts.PrefixUnaryExpression & { operand: ts.NumericLiteral } {
return isPrefixUnaryExpression(node) &&
node.operator === ts.SyntaxKind.MinusToken &&
Expand Down
2 changes: 1 addition & 1 deletion src/rules/strictBooleanExpressionsRule.ts
Expand Up @@ -87,7 +87,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<Options>) => walk(ctx, program.getTypeChecker()), options);
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/rules/strictTypePredicatesRule.ts
Expand Up @@ -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
Expand All @@ -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],
Expand All @@ -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()));
}
}
Expand Down
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"module": "commonjs"
"strict": true,
"strictNullChecks": false
}
}
@@ -1,5 +1,5 @@
{
"compilerOptions": {
"module": "commonjs"
"strictNullChecks": false
}
}
156 changes: 156 additions & 0 deletions test/rules/strict-type-predicates/no-strict-null-checks/test.ts.lint
@@ -0,0 +1,156 @@
declare function get<T>(): T;

// typeof undefined
{
typeof get<boolean>() === "undefined";

typeof get<void>() === "undefined";

typeof get<boolean | undefined>() === "undefined";

declare const c: any;
typeof get<any>() === "undefined";

// 'undefined' is not assignable to '{}'
typeof get<{}>() === "undefined";
}

// typeof boolean
{
declare const a: boolean;
typeof get<boolean>() === "boolean";

typeof get<Boolean>() === "boolean";

typeof get<boolean | undefined>() === "boolean";

typeof get<{}>() === "boolean";
}

// typeof number
{
enum E {}

typeof get<E>() === "number";

typeof get<Number>() === "number";
}

// typeof string
{
typeof get<"abc" | "def">() === "string";

typeof get<String>() === "string";

typeof get<"abc" | undefined>() === "string";
}

// typeof symbol
{
typeof get<symbol>() === "symbol";

typeof get<string>() === "symbol";

typeof get<symbol | string>() === "symbol";
}

// typeof function
{
typeof get<() => void>() === "function";

typeof get<Function>() === "function";


typeof get<number>() === "function";

typeof get<number | (() => void)>() === "function";

class X {}
typeof X === "function";
typeof X === "object";

// Works with union
class Foo { }
typeof get<typeof Foo | object> === "function";
}

// typeof object
{
typeof get<boolean | number | string | symbol | (() => void) | Function>() === "object";

typeof get<{}> === "object";
}

// === null / undefined
{
get<number | undefined>() === null;

get<number | null>() === null;

get<number | null>() === undefined;

get<number | undefined>() === undefined;

// 'null' and 'undefined' are not assignable to '{}'

get<{}>() === null;

get<{}>() === undefined;

get<string | undefined>() == null;

get<string | null>() == undefined;

get<string | null>() == null;

get<string | undefined>() == undefined;

get<string | undefined>() != null;

get<{}>() == null;

get<string | null | undefined>() == null;
get<string | null | undefined>() != undefined;

get<null|undefined>() == null;
get<null|undefined>() != undefined;
}

// negation
{
get<number | undefined>() !== null;

get<number | null>() !== null;

get<number | null>() !== undefined;

get<number | undefined>() !== undefined;

typeof get<string>() !== "string";
}

// reverse left/right
{
"string" === typeof get<number>();

undefined === get<void>();
}

// type parameters
{
function f<T>(t: T) {
typeof t === "boolean";
}

// TODO: Would be nice to catch this.
function g<T extends string>(t: T) {
typeof t === "boolean";
}
}

// Detects bad typeof
{
typeof get<boolean>() === true;

typeof get<any>() === "orbject";
}
@@ -0,0 +1,5 @@
{
"compilerOptions": {
// strictNullChecks not enabled
}
}
@@ -0,0 +1,5 @@
{
"rules": {
"strict-type-predicates": true
}
}