Skip to content
This repository has been archived by the owner on Feb 21, 2022. It is now read-only.

Commit

Permalink
Fixed #11 - added no-extra-boolean-cast rule
Browse files Browse the repository at this point in the history
  • Loading branch information
buzinas committed Nov 13, 2015
1 parent dc411b2 commit a5882da
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ The following rules point out areas where you might have made mistakes.
"no-ex-assign": true
```

* [no-extra-boolean-cast](http://eslint.org/docs/rules/no-extra-boolean-cast) => no-extra-boolean-cast (tslint-eslint-rules) [TODO](https://github.com/buzinas/tslint-eslint-rules/issues/11)
* [no-extra-boolean-cast](http://eslint.org/docs/rules/no-extra-boolean-cast) => no-extra-boolean-cast (tslint-eslint-rules)
* Description: disallow double-negation boolean casts in a boolean context (recommended)
* Usage

Expand Down
3 changes: 2 additions & 1 deletion eslint_tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"use-isnan": true,
"no-duplicate-case": true,
"no-sparse-array": true,
"no-extra-semi": true
"no-extra-semi": true,
"no-extra-boolean-cast": true
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tslint-eslint-rules",
"version": "0.1.7",
"version": "0.1.8",
"description": "Improve your TSLint with the missing ESLint Rules",
"main": "index.js",
"scripts": {
Expand Down
64 changes: 64 additions & 0 deletions src/rules/noExtraBooleanCastRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/// <reference path='helper.d.ts' />
// import {ts, Lint} from './helper';

export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING = {
if: 'redundant double negation in an if statement condition',
do: 'redundant double negation in a do while loop condition',
while: 'redundant double negation in a while loop condition',
ternaryif: 'redundant double negation in a ternary condition',
for: 'redundant double negation in a for loop condition',
unaryCast: 'redundant multiple negation',
objectCast: 'redundant double negation in call to Boolean()',
newCast: 'redundant double negation in Boolean constructor call'
};

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

class NoExtraBooleanCastWalker extends Lint.RuleWalker {
protected visitPrefixUnaryExpression(node: ts.PrefixUnaryExpression) {
this.validateNoExtraBoolean(node);
super.visitPrefixUnaryExpression(node);
}

private validateNoExtraBoolean(node: ts.PrefixUnaryExpression) {
const parent = node.parent;
const grandparent = parent.parent;

// Exit early if it's guaranteed not to match
if (node.operator !== ts.SyntaxKind.ExclamationToken ||
parent.kind !== ts.SyntaxKind.PrefixUnaryExpression ||
(parent as ts.PrefixUnaryExpression).operator !== ts.SyntaxKind.ExclamationToken) {
return;
}

if (grandparent.kind === ts.SyntaxKind.IfStatement) {
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING.if));
}
else if (grandparent.kind === ts.SyntaxKind.DoStatement) {
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING.do));
}
else if (grandparent.kind === ts.SyntaxKind.WhileStatement) {
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING.while));
}
else if (grandparent.kind === ts.SyntaxKind.ConditionalExpression && parent === (grandparent as ts.ConditionalExpression).condition) {
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING.ternaryif));
}
else if (grandparent.kind === ts.SyntaxKind.ForStatement && parent === (grandparent as ts.ForStatement).condition) {
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING.for));
}
else if (grandparent.kind === ts.SyntaxKind.PrefixUnaryExpression && (grandparent as ts.PrefixUnaryExpression).operator === ts.SyntaxKind.ExclamationToken) {
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING.unaryCast));
}
else if (grandparent.kind === ts.SyntaxKind.CallExpression && grandparent.getText().startsWith('Boolean')) {
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING.objectCast));
}
else if (grandparent.kind === ts.SyntaxKind.NewExpression && grandparent.getText().startsWith('new Boolean')) {
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING.newCast));
}
}
}
34 changes: 34 additions & 0 deletions src/test/rules/noExtraBooleanCastRuleTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/// <reference path='../../../typings/mocha/mocha.d.ts' />
import {makeTest} from './helper';

const rule = 'no-extra-boolean-cast';
const scripts = {
valid: [
'if (!foo) {}',
'const x = !foo;',
'const foo = true;',
'const foo = !!bar;',
'function foo() { return !!bar }',
'const foo = bar ? !!x : !!y;`'
],
invalid: [
'if (!!foo) {}',
'const foo = !!!bar;',
'const foo = !!bar ? baz : bat;',
'const foo = Boolean(!!bar);',
'const foo = new Boolean(!!bar);',
'while (!!foo) {}',
'do {} while (!!foo);',
'for (; !!foo; ) {}`'
]
};

describe(rule, function test() {
it('should pass when using valid boolean casts outside of a boolean context', function testValid() {
makeTest(rule, scripts.valid, true);
});

it('should fail when using redundant boolean casts in a boolean context', function testInvalid() {
makeTest(rule, scripts.invalid, false);
});
});

0 comments on commit a5882da

Please sign in to comment.