4 * Copyright 2017 Palantir Technologies, Inc.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 Object.defineProperty(exports, "__esModule", { value: true });
19 var tslib_1 = require("tslib");
20 var tsutils_1 = require("tsutils");
21 var ts = require("typescript");
22 var error_1 = require("../error");
23 var Lint = require("../index");
24 // tslint:disable:no-bitwise
25 var Rule = /** @class */ (function (_super) {
26 tslib_1.__extends(Rule, _super);
28 return _super !== null && _super.apply(this, arguments) || this;
30 Rule.FAILURE_STRING = function (value) {
31 return "Expression is always " + value + ".";
33 Rule.FAILURE_STRICT_PREFER_STRICT_EQUALS = function (value, isPositive) {
34 return "Use '" + (isPositive ? "===" : "!==") + " " + value + "' instead.";
36 Rule.prototype.applyWithProgram = function (sourceFile, program) {
37 if (!Lint.isStrictNullChecksEnabled(program.getCompilerOptions())) {
38 error_1.showWarningOnce("strict-type-predicates does not work without --strictNullChecks");
41 return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
43 /* tslint:disable:object-literal-sort-keys */
45 ruleName: "strict-type-predicates",
46 description: Lint.Utils.dedent(templateObject_1 || (templateObject_1 = tslib_1.__makeTemplateObject(["\n Warns for type predicates that are always true or always false.\n Works for 'typeof' comparisons to constants (e.g. 'typeof foo === \"string\"'), and equality comparison to 'null'/'undefined'.\n (TypeScript won't let you compare '1 === 2', but it has an exception for '1 === undefined'.)\n Does not yet work for 'instanceof'.\n Does *not* warn for 'if (x.y)' where 'x.y' is always truthy. For that, see strict-boolean-expressions.\n\n This rule requires `strictNullChecks` to work properly."], ["\n Warns for type predicates that are always true or always false.\n Works for 'typeof' comparisons to constants (e.g. 'typeof foo === \"string\"'), and equality comparison to 'null'/'undefined'.\n (TypeScript won't let you compare '1 === 2', but it has an exception for '1 === undefined'.)\n Does not yet work for 'instanceof'.\n Does *not* warn for 'if (x.y)' where 'x.y' is always truthy. For that, see strict-boolean-expressions.\n\n This rule requires \\`strictNullChecks\\` to work properly."]))),
47 optionsDescription: "Not configurable.",
49 optionExamples: [true],
50 type: "functionality",
52 requiresTypeInfo: true,
54 /* tslint:enable:object-literal-sort-keys */
55 Rule.FAILURE_STRING_BAD_TYPEOF = "Bad comparison for 'typeof'.";
57 }(Lint.Rules.TypedRule));
59 function walk(ctx, checker) {
60 return ts.forEachChild(ctx.sourceFile, function cb(node) {
61 if (tsutils_1.isBinaryExpression(node)) {
62 var equals = Lint.getEqualsKind(node.operatorToken);
63 if (equals !== undefined) {
64 checkEquals(node, equals);
67 return ts.forEachChild(node, cb);
69 function checkEquals(node, _a) {
70 var isStrict = _a.isStrict, isPositive = _a.isPositive;
71 var exprPred = getTypePredicate(node, isStrict);
72 if (exprPred === undefined) {
75 if (exprPred.kind === 2 /* TypeofTypo */) {
76 fail(Rule.FAILURE_STRING_BAD_TYPEOF);
79 var exprType = checker.getTypeAtLocation(exprPred.expression);
80 // TODO: could use checker.getBaseConstraintOfType to help with type parameters, but it's not publicly exposed.
81 if (tsutils_1.isTypeFlagSet(exprType, ts.TypeFlags.Any | ts.TypeFlags.TypeParameter | ts.TypeFlags.Unknown)) {
84 switch (exprPred.kind) {
86 var predicate = exprPred.predicate, isNullOrUndefined = exprPred.isNullOrUndefined;
87 var value = getConstantBoolean(exprType, predicate);
88 // 'null'/'undefined' are the only two values *not* assignable to '{}'.
89 if (value !== undefined && (isNullOrUndefined || !isEmptyType(checker, exprType))) {
90 fail(Rule.FAILURE_STRING(value === isPositive));
94 case 1 /* NonStructNullUndefined */: {
95 var result = testNonStrictNullUndefined(exprType);
96 if (result !== undefined) {
97 fail(typeof result === "boolean"
98 ? Rule.FAILURE_STRING(result === isPositive)
99 : Rule.FAILURE_STRICT_PREFER_STRICT_EQUALS(result, isPositive));
103 function fail(failure) {
104 ctx.addFailureAtNode(node, failure);
108 /** Detects a type predicate given `left === right`. */
109 function getTypePredicate(node, isStrictEquals) {
110 var left = node.left, right = node.right;
111 var lr = getTypePredicateOneWay(left, right, isStrictEquals);
112 return lr !== undefined ? lr : getTypePredicateOneWay(right, left, isStrictEquals);
114 /** Only gets the type predicate if the expression is on the left. */
115 function getTypePredicateOneWay(left, right, isStrictEquals) {
116 switch (right.kind) {
117 case ts.SyntaxKind.TypeOfExpression:
118 var expression = right.expression;
119 if (!tsutils_1.isLiteralExpression(left)) {
120 if ((tsutils_1.isIdentifier(left) && left.text === "undefined") ||
121 left.kind === ts.SyntaxKind.NullKeyword ||
122 left.kind === ts.SyntaxKind.TrueKeyword ||
123 left.kind === ts.SyntaxKind.FalseKeyword) {
124 return { kind: 2 /* TypeofTypo */ };
128 var predicate = getTypePredicateForKind(left.text);
129 return predicate === undefined
130 ? { kind: 2 /* TypeofTypo */ }
132 expression: expression,
133 isNullOrUndefined: left.text === "undefined",
135 predicate: predicate,
137 case ts.SyntaxKind.NullKeyword:
138 return nullOrUndefined(ts.TypeFlags.Null);
139 case ts.SyntaxKind.Identifier:
140 if (right.originalKeywordKind === ts.SyntaxKind.UndefinedKeyword) {
141 return nullOrUndefined(undefinedFlags);
147 function nullOrUndefined(flags) {
148 return isStrictEquals
151 isNullOrUndefined: true,
153 predicate: flagPredicate(flags),
155 : { kind: 1 /* NonStructNullUndefined */, expression: left };
158 function isEmptyType(checker, type) {
159 return checker.typeToString(type) === "{}";
161 var undefinedFlags = ts.TypeFlags.Undefined | ts.TypeFlags.Void;
162 function getTypePredicateForKind(kind) {
165 return flagPredicate(undefinedFlags);
167 return flagPredicate(ts.TypeFlags.BooleanLike);
169 return flagPredicate(ts.TypeFlags.NumberLike);
171 return flagPredicate(ts.TypeFlags.StringLike);
173 return flagPredicate(ts.TypeFlags.ESSymbol);
177 // It's an object if it's not any of the above.
178 var allFlags_1 = ts.TypeFlags.Undefined |
180 ts.TypeFlags.BooleanLike |
181 ts.TypeFlags.NumberLike |
182 ts.TypeFlags.StringLike |
183 ts.TypeFlags.ESSymbol;
184 return function (type) { return !tsutils_1.isTypeFlagSet(type, allFlags_1) && !isFunction(type); };
189 function flagPredicate(testedFlag) {
190 return function (type) { return tsutils_1.isTypeFlagSet(type, testedFlag); };
192 function isFunction(t) {
193 if (t.getConstructSignatures().length !== 0 || t.getCallSignatures().length !== 0) {
196 var symbol = t.getSymbol();
197 return symbol !== undefined && symbol.getName() === "Function";
199 /** Returns a boolean value if that should always be the result of a type predicate. */
200 function getConstantBoolean(type, predicate) {
202 var anyFalse = false;
203 for (var _i = 0, _a = unionParts(type); _i < _a.length; _i++) {
211 if (anyTrue && anyFalse) {
217 /** Returns bool for always/never true, or a string to recommend strict equality. */
218 function testNonStrictNullUndefined(type) {
220 var anyUndefined = false;
221 var anyOther = false;
222 for (var _i = 0, _a = unionParts(type); _i < _a.length; _i++) {
224 if (tsutils_1.isTypeFlagSet(ty, ts.TypeFlags.Null)) {
227 else if (tsutils_1.isTypeFlagSet(ty, undefinedFlags)) {
236 : anyNull && anyUndefined
244 function unionParts(type) {
245 return tsutils_1.isUnionType(type) ? type.types : [type];
247 var templateObject_1;