1
votes

I've copied this tslint rule into my project and got it working so now tslint picks up any === null and makes it an error.

I now want to do the same thing but for the undefined. I have implemented it the same as the null one but changed the check to look for ts.SyntaxKind.UndefinedKeyWord instead of ts.SyntaxKind.NullKeyword and it for some reason doesn't pick up on === undefined.

Why not if it is the same as the null one which is working?

noTripleEqualsNullRule.ts

import * as ts from "typescript";
import * as Lint from "tslint";

const OPTION_NO_NULL_KEYWORD = "no-null-keyword";

export class Rule extends Lint.Rules.AbstractRule {
    public static EQ_FAILURE_STRING = "Did you mean == null instead?";
    public static NEQ_FAILURE_STRING = "Did you mean != null instead?";

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

class NoTripleEqualsNullWalker extends Lint.RuleWalker {
    public visitBinaryExpression(node: ts.BinaryExpression) {
        if (this.isExpressionAllowed(node)) {
            const position = node.getChildAt(1).getStart();
            const expressionWidth = node.right.getFullWidth() + 3;
            this.handleBinaryComparison(position, expressionWidth, node.operatorToken.kind, node.right.kind);
        }

        super.visitBinaryExpression(node);
    }

    private handleBinaryComparison(position: number, expressionWidth: number, operator: ts.SyntaxKind, right: ts.SyntaxKind) {
        switch (operator) {
            case ts.SyntaxKind.EqualsEqualsEqualsToken:
                if (right === ts.SyntaxKind.NullKeyword) {
                    this.addFailure(this.createFailure(position, expressionWidth, Rule.EQ_FAILURE_STRING));
                }
                break;
            case ts.SyntaxKind.ExclamationEqualsEqualsToken:
                if (right === ts.SyntaxKind.NullKeyword) {
                    this.addFailure(this.createFailure(position, expressionWidth, Rule.NEQ_FAILURE_STRING));
                }
                break;
            default:
                break;
        }
    }

    private isExpressionAllowed(node: ts.BinaryExpression) {
        const nullKeyword = ts.SyntaxKind.NullKeyword;

        return !this.hasOption(OPTION_NO_NULL_KEYWORD)
            && (node.left.kind ===  nullKeyword || node.right.kind === nullKeyword);
    }
}

noTripleEqualsUndefinedRule.ts

import * as ts from "typescript";
import * as Lint from "tslint";

const OPTION_NO_NULL_KEYWORD = "no-null-keyword";

export class Rule extends Lint.Rules.AbstractRule {
    public static EQ_FAILURE_STRING = "Did you mean == null instead?";
    public static NEQ_FAILURE_STRING = "Did you mean != null instead?";

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

class NoTripleEqualsNullWalker extends Lint.RuleWalker {
    public visitBinaryExpression(node: ts.BinaryExpression) {
        if (this.isExpressionAllowed(node)) {
            const position = node.getChildAt(1).getStart();
            const expressionWidth = node.right.getFullWidth() + 3;
            this.handleBinaryComparison(position, expressionWidth, node.operatorToken.kind, node.right.kind);
        }

        super.visitBinaryExpression(node);
    }

    private handleBinaryComparison(position: number, expressionWidth: number, operator: ts.SyntaxKind, right: ts.SyntaxKind) {
        switch (operator) {
            case ts.SyntaxKind.EqualsEqualsEqualsToken:
                if (right === ts.SyntaxKind.UndefinedKeyword) {
                    this.addFailure(this.createFailure(position, expressionWidth, Rule.EQ_FAILURE_STRING));
                }
                break;
            case ts.SyntaxKind.ExclamationEqualsEqualsToken:
                if (right === ts.SyntaxKind.UndefinedKeyword) {
                    this.addFailure(this.createFailure(position, expressionWidth, Rule.NEQ_FAILURE_STRING));
                }
                break;
            default:
                break;
        }
    }

    private isExpressionAllowed(node: ts.BinaryExpression) {
        const nullKeyword = ts.SyntaxKind.UndefinedKeyword;

        return !this.hasOption(OPTION_NO_NULL_KEYWORD)
            && (node.left.kind ===  nullKeyword || node.right.kind === nullKeyword);
    }
}
1
undefined is not the same as null please read developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…jshamble
@jshamble I know undefined is not the same as null. That's why I created a new tslint rule for undefined, not null.BeniaminoBaggins

1 Answers

1
votes

The linter thinks that the kind of undefined is SyntaxKind.Identifier rather than SyntaxKind.UndefinedKeyword. This kind of makes sense, since undefined can be redefined – but is still unfortunate.

If you're sure that undefined has not been redefined anywhere in your codebase, you could check node.getText() === "undefined" instead.