diff --git a/README.md b/README.md
index ea6689a38..96f4fd425 100644
--- a/README.md
+++ b/README.md
@@ -722,6 +722,15 @@ We recommend you specify exact versions of lint libraries, including `tslint-mic
no-var-self
diff --git a/recommended_ruleset.js b/recommended_ruleset.js
index 4d523fdbf..a62c1cfac 100644
--- a/recommended_ruleset.js
+++ b/recommended_ruleset.js
@@ -181,6 +181,7 @@ module.exports = {
'unified-signatures': true,
'use-default-type-parameter': true,
'variable-name': true,
+ 'void-zero': true,
/**
* Accessibility. The following rules should be turned on to guarantee the best user
diff --git a/src/voidZeroRule.ts b/src/voidZeroRule.ts
new file mode 100644
index 000000000..4252033a0
--- /dev/null
+++ b/src/voidZeroRule.ts
@@ -0,0 +1,47 @@
+import * as ts from 'typescript';
+import * as Lint from 'tslint';
+import * as tsutils from 'tsutils';
+
+import { ExtendedMetadata } from './utils/ExtendedMetadata';
+
+const FAILURE_STRING: string = 'Replace void 0 with undefined';
+
+export class Rule extends Lint.Rules.AbstractRule {
+ public static metadata: ExtendedMetadata = {
+ ruleName: 'void-zero',
+ type: 'maintainability',
+ description: 'Avoid using void 0; use undefined instead.',
+ hasFix: true,
+ rationale: 'void 0, which resolves to undefined, can be confusing to newcomers. Exclusively use undefined to reduce ambiguity.',
+ options: null, // tslint:disable-line:no-null-keyword
+ optionsDescription: '',
+ typescriptOnly: true,
+ issueClass: 'Non-SDL',
+ issueType: 'Warning',
+ severity: 'Low',
+ level: 'Opportunity for Excellence',
+ group: 'Clarity',
+ commonWeaknessEnumeration: '480'
+ };
+
+ public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
+ return this.applyWithFunction(sourceFile, walk);
+ }
+}
+
+function walk(ctx: Lint.WalkContext) {
+ function cb(node: ts.Node): void {
+ if (tsutils.isVoidExpression(node)) {
+ if (node.expression !== undefined && node.expression.getText() === '0') {
+ const nodeStart = node.getStart();
+ const nodeWidth = node.getWidth();
+ const fix = new Lint.Replacement(nodeStart, nodeWidth, 'undefined');
+
+ ctx.addFailureAt(nodeStart, nodeWidth, FAILURE_STRING, fix);
+ }
+ }
+ return ts.forEachChild(node, cb);
+ }
+
+ return ts.forEachChild(ctx.sourceFile, cb);
+}
diff --git a/tests/void-zero/test.ts.fix b/tests/void-zero/test.ts.fix
new file mode 100644
index 000000000..8c5023dcc
--- /dev/null
+++ b/tests/void-zero/test.ts.fix
@@ -0,0 +1,22 @@
+new Array(undefined);
+function mockFunction(arg1: undefined, arg2: string) {}
+mockFunction(undefined, 'arg2String')
+const bar = undefined;
+const foo = {
+ bar: undefined,
+};
+class MockClass {
+ private foo = undefined
+ static bar = undefined
+}
+
+new Array(undefined);
+mockFunction(undefined, 'arg2String')
+const bar = undefined;
+const foo = {
+ bar: undefined,
+};
+class MockClass {
+ private foo = undefined
+ static bar = undefined
+}
diff --git a/tests/void-zero/test.ts.lint b/tests/void-zero/test.ts.lint
new file mode 100644
index 000000000..2c15c56da
--- /dev/null
+++ b/tests/void-zero/test.ts.lint
@@ -0,0 +1,28 @@
+new Array(undefined);
+function mockFunction(arg1: undefined, arg2: string) {}
+mockFunction(undefined, 'arg2String')
+const bar = undefined;
+const foo = {
+ bar: undefined,
+};
+class MockClass {
+ private foo = undefined
+ static bar = undefined
+}
+
+new Array(void 0);
+ ~~~~~~ [Replace void 0 with undefined]
+mockFunction(void 0, 'arg2String')
+ ~~~~~~ [Replace void 0 with undefined]
+const bar = void 0;
+ ~~~~~~ [Replace void 0 with undefined]
+const foo = {
+ bar: void 0,
+ ~~~~~~ [Replace void 0 with undefined]
+};
+class MockClass {
+ private foo = void 0
+ ~~~~~~ [Replace void 0 with undefined]
+ static bar = void 0
+ ~~~~~~ [Replace void 0 with undefined]
+}
diff --git a/tests/void-zero/tslint.json b/tests/void-zero/tslint.json
new file mode 100644
index 000000000..dc0c36389
--- /dev/null
+++ b/tests/void-zero/tslint.json
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "void-zero": true
+ }
+}
diff --git a/tslint-warnings.csv b/tslint-warnings.csv
index 2570d126a..5e3be3356 100644
--- a/tslint-warnings.csv
+++ b/tslint-warnings.csv
@@ -319,5 +319,6 @@ use-simple-attributes,Enforce usage of only simple attribute types.,TSLINT1PG0L9
valid-typeof,Ensures that the results of typeof are compared against a valid string.,TSLINT1IB59P1,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,,
variable-name,Checks variable names for various errors.,TSLINT1CIV7K3,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality
CWE 710 - Coding Standards Violation"
+void-zero,Avoid using void 0; use undefined instead.,TSLINT1BDGNG2,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,480,"CWE 480 - Use of Incorrect Operator"
whitespace,Enforces whitespace style conventions.,TSLINTC35UUS,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality
CWE 710 - Coding Standards Violation"
\ No newline at end of file
diff --git a/tslint.json b/tslint.json
index 7ef1eb817..a1e66a427 100644
--- a/tslint.json
+++ b/tslint.json
@@ -148,6 +148,7 @@
"switch-final-break": true,
"type-literal-delimiter": false,
"underscore-consistent-invocation": true,
+ "void-zero": true,
"use-default-type-parameter": false,
"use-named-parameter": true,
"use-simple-attributes": true,
|