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

Add typeof-compare rule #1927

Merged
merged 8 commits into from
Jan 1, 2017
Merged
Show file tree
Hide file tree
Changes from 5 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
9 changes: 9 additions & 0 deletions docs/_data/rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,15 @@
"type": "typescript",
"typescriptOnly": true
},
{
"ruleName": "typeof-compare",
"description": "Makes sure result of `typeof` is compared to correct string values",
"optionsDescription": "",
"options": {},
"optionExamples": [],
"type": "functionality",
"typescriptOnly": false
},
{
"ruleName": "use-isnan",
"description": "Enforces use of the `isNaN()` function to check for NaN references instead of a comparison to the `NaN` constant.",
Expand Down
12 changes: 12 additions & 0 deletions docs/rules/typeof-compare/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
ruleName: typeof-compare
description: Makes sure result of `typeof` is compared to correct string values
optionsDescription: ''
options: {}
optionExamples: []
type: functionality
typescriptOnly: false
layout: rule
title: 'Rule: typeof-compare'
optionsJSON: '{}'
---
96 changes: 96 additions & 0 deletions src/rules/typeofCompareRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* @license
* Copyright 2013 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as ts from "typescript";

import * as Lint from "../index";

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
ruleName: "typeof-compare",
description: "Makes sure result of `typeof` is compared to correct string values",
optionsDescription: "",
options: {},
optionExamples: [],
type: "functionality",
typescriptOnly: false,
};
/* tslint:enable:object-literal-sort-keys */

public static FAILURE_STRING = "typeof must be compared to correct value";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
const comparisonWalker = new ComparisonWalker(sourceFile, this.getOptions());
return this.applyWithWalker(comparisonWalker);
}
}

class ComparisonWalker extends Lint.RuleWalker {
private static LEGAL_TYPEOF_RESULTS = ["undefined", "string", "boolean", "number", "function", "object", "symbol"];

private static isCompareOperator(node: ts.Node): boolean {
return node.kind === ts.SyntaxKind.EqualsEqualsToken || node.kind === ts.SyntaxKind.EqualsEqualsEqualsToken;
}

private static isLegalStringLiteral(node: ts.Node) {
return ComparisonWalker.LEGAL_TYPEOF_RESULTS.indexOf(node.getText()) > -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's failing here because node.getText() includes the quotes. For example, "string" instead of what you want: string. If you use node.text, it will exclude the quotes

}

private static isFaultyOtherSideOfTypeof(node: ts.Node): boolean {
switch (node.kind) {
case ts.SyntaxKind.StringLiteral:
if (!ComparisonWalker.isLegalStringLiteral(node)) {
return true;
}
break;
case ts.SyntaxKind.Identifier:
if ((<ts.Identifier> node).originalKeywordKind === ts.SyntaxKind.UndefinedKeyword) {
return true;
}
break;
case ts.SyntaxKind.NullKeyword:
case ts.SyntaxKind.FirstLiteralToken:
case ts.SyntaxKind.TrueKeyword:
case ts.SyntaxKind.FalseKeyword:
case ts.SyntaxKind.ObjectLiteralExpression:
case ts.SyntaxKind.ArrayLiteralExpression:
return true;
default: break;
}
return false;
}

public visitBinaryExpression(node: ts.BinaryExpression) {
let isFaulty = false;
if (ComparisonWalker.isCompareOperator(node.operatorToken)) {
// typeof is at left
if (node.left.kind === ts.SyntaxKind.TypeOfExpression && ComparisonWalker.isFaultyOtherSideOfTypeof(node.right)) {
isFaulty = true;
}
// typeof is at right
if (node.right.kind === ts.SyntaxKind.TypeOfExpression && ComparisonWalker.isFaultyOtherSideOfTypeof(node.left)) {
isFaulty = true;
}
}
if (isFaulty) {
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING));
}
super.visitBinaryExpression(node);
}

}
38 changes: 38 additions & 0 deletions test/rules/typeof-compare/test.js.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
var testVariable = 123;

function testFunction() {
if (typeof x === 'null') {
~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
console.log("called");
}
const x = typeof foo === 'undefied'
~~~~~~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
}

console.log(typeof window == 'object ')
~~~~~~~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]

const string = "string";
typeof bar === string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs more positive tests. Currently only tests against a string variable. Needs to test against directly against a string, against a template string, and a template string with a replacement variable. Also exercise different types of valid types


typeof bar === 123
~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
typeof bar === undefined
~~~~~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
typeof bar === null
~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
typeof bar === true
~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
typeof bar === false
~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]

123 === typeof bar
~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
undefined === typeof bar
~~~~~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
null === typeof bar
~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
true === typeof bar
~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
false === typeof bar
~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
38 changes: 38 additions & 0 deletions test/rules/typeof-compare/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
var testVariable = 123;

function testFunction() {
if (typeof x === 'null') {
~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
console.log("called");
}
const x = typeof foo === 'undefied'
~~~~~~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
}

console.log(typeof window == 'object ')
~~~~~~~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]

const string = "string";
typeof bar === string

typeof bar === 123
~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
typeof bar === undefined
~~~~~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
typeof bar === null
~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
typeof bar === true
~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
typeof bar === false
~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]

123 === typeof bar
~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
undefined === typeof bar
~~~~~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
null === typeof bar
~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
true === typeof bar
~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
false === typeof bar
~~~~~~~~~~~~~~~~~~~~ [typeof must be compared to correct value]
8 changes: 8 additions & 0 deletions test/rules/typeof-compare/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"rules": {
"typeof-compare": true
},
"jsRules": {
"typeof-compare": true
}
}