diff --git a/Gruntfile.js b/Gruntfile.js index adf6b04c9..572bc1fd8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -320,12 +320,15 @@ module.exports = function(grunt) { 'cyclomatic-complexity': true, // too strict 'deprecation': true, // requires type checking 'file-header': true, // no need + 'file-name-casing': true, // too strict 'interface-name': true, // no need 'match-default-export-name': true, // requires type checking 'max-classes-per-file': true, // no need 'max-file-line-count': true, // no need 'member-ordering': true, // too strict 'newline-before-return': true, // kind of a silly rule + 'newline-per-chained-call': true, // too strict + 'no-dynamic-delete': true, // too strict 'no-empty-line-after-opening-brace': true, // too strict 'no-inferrable-types': true, // we prefer the opposite 'no-multiline-string': true, // too strict @@ -339,6 +342,7 @@ module.exports = function(grunt) { 'prefer-conditional-expression': true, // not sure if this is needed 'prefer-switch': true, // no need 'prefer-template': true, // rule does not handle multi-line strings nicely + 'prefer-while': true, // not sure if this is needed 'return-undefined': true, // requires type checking 'type-literal-delimiter': true, // not sure if this is needed 'typedef-whitespace': true, // too strict diff --git a/additional_rule_metadata.json b/additional_rule_metadata.json index 13f5130bc..7e844a6dd 100644 --- a/additional_rule_metadata.json +++ b/additional_rule_metadata.json @@ -57,6 +57,14 @@ "group": "Whitespace", "commonWeaknessEnumeration": "710" }, + "file-name-casing": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Moderate", + "level": "Opportunity for Excellence", + "group": "Clarity", + "commonWeaknessEnumeration": "398, 710" + }, "forin": { "issueClass": "Non-SDL", "issueType": "Warning", @@ -156,6 +164,14 @@ "group": "Clarity", "commonWeaknessEnumeration": "710" }, + "newline-per-chained-call": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Moderate", + "level": "Opportunity for Excellence", + "group": "Clarity", + "commonWeaknessEnumeration": "398, 710" + }, "no-angle-bracket-type-assertion": { "issueClass": "Ignored", "issueType": "Warning", @@ -254,6 +270,14 @@ "group": "Correctness", "commonWeaknessEnumeration": "398" }, + "no-dynamic-delete": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Moderate", + "level": "Opportunity for Excellence", + "group": "Clarity", + "commonWeaknessEnumeration": "398, 710" + }, "no-empty": { "issueClass": "Non-SDL", "issueType": "Warning", @@ -498,6 +522,22 @@ "group": "Clarity", "commonWeaknessEnumeration": "398, 710" }, + "prefer-readonly": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Moderate", + "level": "Opportunity for Excellence", + "group": "Clarity", + "commonWeaknessEnumeration": "398, 710" + }, + "prefer-while": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Moderate", + "level": "Opportunity for Excellence", + "group": "Clarity", + "commonWeaknessEnumeration": "398, 710" + }, "quotemark": { "issueClass": "Non-SDL", "issueType": "Warning", diff --git a/package-lock.json b/package-lock.json index 99ffd893a..1919d05ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -956,23 +956,21 @@ "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -985,20 +983,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -1039,7 +1034,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -1054,14 +1049,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { @@ -1070,12 +1065,12 @@ "dev": true, "optional": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { @@ -1090,7 +1085,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": "^2.1.0" } }, "ignore-walk": { @@ -1099,7 +1094,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -1108,15 +1103,14 @@ "dev": true, "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -1128,9 +1122,8 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { @@ -1143,25 +1136,22 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" } }, "minizlib": { @@ -1170,14 +1160,13 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "mkdirp": { "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -1194,9 +1183,9 @@ "dev": true, "optional": true, "requires": { - "debug": "2.6.9", - "iconv-lite": "0.4.21", - "sax": "1.2.4" + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "node-pre-gyp": { @@ -1205,16 +1194,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.2.0", - "nopt": "4.0.1", - "npm-packlist": "1.1.10", - "npmlog": "4.1.2", - "rc": "1.2.7", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "4.4.1" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { @@ -1223,8 +1212,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { @@ -1239,8 +1228,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { @@ -1249,17 +1238,16 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -1271,9 +1259,8 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -1294,8 +1281,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -1316,10 +1303,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "0.5.1", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -1336,13 +1323,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "rimraf": { @@ -1351,7 +1338,7 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-buffer": { @@ -1393,11 +1380,10 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -1406,7 +1392,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { @@ -1414,7 +1400,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -1429,13 +1415,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "1.0.1", - "fs-minipass": "1.2.5", - "minipass": "2.2.4", - "minizlib": "1.1.0", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" } }, "util-deprecate": { @@ -1450,7 +1436,7 @@ "dev": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2" } }, "wrappy": { @@ -2342,9 +2328,9 @@ } }, "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", + "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", "dev": true, "optional": true }, @@ -2516,9 +2502,9 @@ "dev": true }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "path-type": { @@ -3192,24 +3178,37 @@ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" }, "tslint": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.8.0.tgz", - "integrity": "sha1-H0mtWy53x2w69N3K5VKuTjYS6xM=", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", + "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", "dev": true, "requires": { "babel-code-frame": "^6.22.0", "builtin-modules": "^1.1.1", - "chalk": "^2.1.0", - "commander": "^2.9.0", + "chalk": "^2.3.0", + "commander": "^2.12.1", "diff": "^3.2.0", "glob": "^7.1.1", + "js-yaml": "^3.7.0", "minimatch": "^3.0.4", "resolve": "^1.3.2", "semver": "^5.3.0", - "tslib": "^1.7.1", - "tsutils": "^2.12.1" + "tslib": "^1.8.0", + "tsutils": "^2.27.2" }, "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -3224,6 +3223,16 @@ "path-is-absolute": "^1.0.0" } }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "resolve": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", @@ -3250,9 +3259,9 @@ "dev": true }, "typescript": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", - "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.1.tgz", + "integrity": "sha512-zQIMOmC+372pC/CCVLqnQ0zSBiY7HHodU7mpQdjiZddek4GMj31I3dUJ7gAs9o65X7mnRma6OokOkc6f9jjfBg==", "dev": true }, "underscore": { diff --git a/package.json b/package.json index e8e24926d..3610c7b0d 100644 --- a/package.json +++ b/package.json @@ -54,8 +54,8 @@ "load-grunt-tasks": "3.2.0", "mocha": "4.0.1", "time-grunt": "1.2.1", - "tslint": "5.8.0", - "typescript": "2.6.2", + "tslint": "^5.11.0", + "typescript": "^3.0.1", "underscore": "1.8.3" }, "peerDependencies": { diff --git a/recommended_ruleset.js b/recommended_ruleset.js index 0b6bc02a1..3ccf7a760 100644 --- a/recommended_ruleset.js +++ b/recommended_ruleset.js @@ -113,6 +113,7 @@ module.exports = { "comment-format": true, "completed-docs": [true, "classes"], "export-name": true, + "file-name-casing": true, "function-name": true, "import-name": true, "interface-name": true, @@ -126,9 +127,11 @@ module.exports = { "missing-jsdoc": true, "mocha-unneeded-done": true, "new-parens": true, + "newline-per-chained-call": true, "no-construct": true, "no-default-export": true, "no-duplicate-imports": true, + "no-dynamic-delete": true, "no-empty-interface": true, "no-for-in": true, "no-function-expression": true, @@ -164,7 +167,9 @@ module.exports = { "prefer-for-of": true, "prefer-method-signature": true, "prefer-object-spread": true, + "prefer-readonly": true, "prefer-template": true, + "prefer-while": true, "type-literal-delimiter": true, "typedef": [true, "call-signature", "arrow-call-signature", "parameter", "arrow-parameter", "property-declaration", "variable-declaration", "member-variable-declaration"], "underscore-consistent-invocation": true, diff --git a/src/chaiPreferContainsToIndexOfRule.ts b/src/chaiPreferContainsToIndexOfRule.ts index b16d6e763..ecef0cea6 100644 --- a/src/chaiPreferContainsToIndexOfRule.ts +++ b/src/chaiPreferContainsToIndexOfRule.ts @@ -61,8 +61,8 @@ class ChaiPreferContainsToIndexOfRuleWalker extends ErrorTolerantWalker { } private isFirstArgumentIndexOfResult(node: ts.CallExpression): boolean { - const expectCall: ts.CallExpression = ChaiUtils.getLeftMostCallExpression(node); - if (expectCall.arguments != null && expectCall.arguments.length > 0) { + const expectCall = ChaiUtils.getLeftMostCallExpression(node); + if (expectCall !== null && expectCall.arguments != null && expectCall.arguments.length > 0) { const firstArgument: ts.Expression = expectCall.arguments[0]; if (firstArgument.kind === ts.SyntaxKind.CallExpression) { if (AstUtils.getFunctionName(firstArgument) === 'indexOf') { diff --git a/src/chaiVagueErrorsRule.ts b/src/chaiVagueErrorsRule.ts index 224793c92..83364b9ce 100644 --- a/src/chaiVagueErrorsRule.ts +++ b/src/chaiVagueErrorsRule.ts @@ -41,7 +41,7 @@ class ChaiVagueErrorsRuleWalker extends ErrorTolerantWalker { protected visitPropertyAccessExpression(node: ts.PropertyAccessExpression): void { if (ChaiUtils.isExpectInvocation(node)) { if (/ok|true|false|undefined|null/.test(node.name.getText())) { - const expectInvocation: ts.CallExpression = ChaiUtils.getExpectInvocation(node); + const expectInvocation = ChaiUtils.getExpectInvocation(node); if (!expectInvocation || expectInvocation.arguments.length !== 2) { this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING); } @@ -62,18 +62,20 @@ class ChaiVagueErrorsRuleWalker extends ErrorTolerantWalker { } } - const actualValue: ts.Node = ChaiUtils.getFirstExpectCallParameter(node); - if (actualValue.kind === ts.SyntaxKind.BinaryExpression) { - const expectedValue: ts.Node = ChaiUtils.getFirstExpectationParameter(node); - const binaryExpression: ts.BinaryExpression = actualValue; - const operator: string = binaryExpression.operatorToken.getText(); - const expectingBooleanKeyword: boolean = expectedValue.kind === ts.SyntaxKind.TrueKeyword - || expectedValue.kind === ts.SyntaxKind.FalseKeyword; + const actualValue = ChaiUtils.getFirstExpectCallParameter(node); + if (actualValue !== null && actualValue.kind === ts.SyntaxKind.BinaryExpression) { + const expectedValue = ChaiUtils.getFirstExpectationParameter(node); + if (expectedValue !== null) { + const binaryExpression: ts.BinaryExpression = actualValue; + const operator: string = binaryExpression.operatorToken.getText(); + const expectingBooleanKeyword: boolean = expectedValue.kind === ts.SyntaxKind.TrueKeyword + || expectedValue.kind === ts.SyntaxKind.FalseKeyword; - if (operator === '===' && expectingBooleanKeyword) { - this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING_COMPARE_TRUE); - } else if (operator === '!==' && expectingBooleanKeyword) { - this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING_COMPARE_FALSE); + if (operator === '===' && expectingBooleanKeyword) { + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING_COMPARE_TRUE); + } else if (operator === '!==' && expectingBooleanKeyword) { + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING_COMPARE_FALSE); + } } } } diff --git a/src/exportNameRule.ts b/src/exportNameRule.ts index 5d86550f6..d2edeb31f 100644 --- a/src/exportNameRule.ts +++ b/src/exportNameRule.ts @@ -34,7 +34,7 @@ export class Rule extends Lint.Rules.AbstractRule { } /* tslint:disable:function-name */ - public static getExceptions(options : Lint.IOptions) : string[] { + public static getExceptions(options : Lint.IOptions): string[] | null { /* tslint:enable:function-name */ if (options.ruleArguments instanceof Array) { return options.ruleArguments[0]; @@ -47,7 +47,7 @@ export class Rule extends Lint.Rules.AbstractRule { } function isExportedDeclaration(element: ts.Statement): boolean { - return AstUtils.hasModifier(element.modifiers, ts.SyntaxKind.ExportKeyword); + return element.modifiers !== undefined && AstUtils.hasModifier(element.modifiers, ts.SyntaxKind.ExportKeyword); } type ExportStatement = ts.ExportDeclaration | ts.ExportAssignment; @@ -60,7 +60,7 @@ function getExportsFromStatement(node: ExportStatement): [string, ts.Node][] { return [[node.expression.getText(), node.expression]]; } else { const symbolAndNodes: [string, ts.Node][] = []; - node.exportClause.elements.forEach(e => { + node.exportClause!.elements.forEach(e => { symbolAndNodes.push([e.name.getText(), node]); }); return symbolAndNodes; @@ -88,7 +88,7 @@ export class ExportNameWalker extends ErrorTolerantWalker { if (exportedTopLevelElements.length === 0) { node.statements.forEach((element: ts.Statement): void => { if (element.kind === ts.SyntaxKind.ModuleDeclaration) { - const exportStatements: ts.Statement[] = this.getExportStatementsWithinModules((element)); + const exportStatements = this.getExportStatementsWithinModules((element)) || []; exportedTopLevelElements = exportedTopLevelElements.concat(exportStatements); } }); @@ -96,11 +96,11 @@ export class ExportNameWalker extends ErrorTolerantWalker { this.validateExportedElements(exportedTopLevelElements); } - private getExportStatementsWithinModules(moduleDeclaration: ts.ModuleDeclaration): ts.Statement[] { - if (moduleDeclaration.body.kind === ts.SyntaxKind.ModuleDeclaration) { + private getExportStatementsWithinModules(moduleDeclaration: ts.ModuleDeclaration): ts.Statement[] | null { + if (moduleDeclaration.body!.kind === ts.SyntaxKind.ModuleDeclaration) { // modules may be nested so recur into the structure return this.getExportStatementsWithinModules(moduleDeclaration.body); - } else if (moduleDeclaration.body.kind === ts.SyntaxKind.ModuleBlock) { + } else if (moduleDeclaration.body!.kind === ts.SyntaxKind.ModuleBlock) { const moduleBlock: ts.ModuleBlock = moduleDeclaration.body; return moduleBlock.statements.filter(isExportedDeclaration); } @@ -145,9 +145,9 @@ export class ExportNameWalker extends ErrorTolerantWalker { } private isSuppressed(exportedName: string) : boolean { - const allExceptions : string[] = Rule.getExceptions(this.getOptions()); + const allExceptions = Rule.getExceptions(this.getOptions()); - return Utils.exists(allExceptions, (exception: string) : boolean => { + return Utils.exists(allExceptions, (exception: string): boolean => { return new RegExp(exception).test(exportedName); }); } diff --git a/src/importNameRule.ts b/src/importNameRule.ts index 5b5c0785e..7d6c083e2 100644 --- a/src/importNameRule.ts +++ b/src/importNameRule.ts @@ -73,8 +73,8 @@ class ImportNameRuleWalker extends ErrorTolerantWalker { } protected visitImportDeclaration(node: ts.ImportDeclaration): void { - if (node.importClause.name != null) { - const name: string = node.importClause.name.text; + if (node.importClause!.name != null) { + const name: string = node.importClause!.name!.text; if (node.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral) { const moduleName: string = (node.moduleSpecifier).text; this.validateImport(node, name, moduleName); @@ -88,10 +88,11 @@ class ImportNameRuleWalker extends ErrorTolerantWalker { moduleName = this.makeCamelCase(moduleName); if (this.isImportNameValid(importedName, moduleName) === false) { const message: string = `Misnamed import. Import should be named '${moduleName}' but found '${importedName}'`; - const nameNode = node.kind === ts.SyntaxKind.ImportEqualsDeclaration ? - (node).name : (node).importClause.name; - const nameNodeStartPos = nameNode.getStart(); - const fix = new Lint.Replacement(nameNodeStartPos, nameNode.end - nameNodeStartPos, moduleName); + const nameNode = node.kind === ts.SyntaxKind.ImportEqualsDeclaration + ? (node).name + : (node).importClause!.name; + const nameNodeStartPos = nameNode!.getStart(); + const fix = new Lint.Replacement(nameNodeStartPos, nameNode!.end - nameNodeStartPos, moduleName); this.addFailureAt(node.getStart(), node.getWidth(), message, fix); } } diff --git a/src/jqueryDeferredMustCompleteRule.ts b/src/jqueryDeferredMustCompleteRule.ts index 3d36e31ed..cd1e3c9ae 100644 --- a/src/jqueryDeferredMustCompleteRule.ts +++ b/src/jqueryDeferredMustCompleteRule.ts @@ -40,7 +40,7 @@ function isPromiseInstantiation(expression: ts.Expression) : boolean { const functionName = AstUtils.getFunctionName(expression); const functionTarget = AstUtils.getFunctionTarget(expression); - if (functionName === 'Deferred' && AstUtils.isJQuery(functionTarget)) { + if (functionName === 'Deferred' && functionTarget !== null && AstUtils.isJQuery(functionTarget)) { return true; } } @@ -65,7 +65,7 @@ class JQueryDeferredAnalyzer extends ErrorTolerantWalker { } protected visitVariableDeclaration(node: ts.VariableDeclaration): void { - if (isPromiseInstantiation(node.initializer)) { + if (node.initializer !== undefined && isPromiseInstantiation(node.initializer)) { if ((node.name).text != null) { const name : ts.Identifier = node.name; this.validateDeferredUsage(node, name); diff --git a/src/maxFuncBodyLengthRule.ts b/src/maxFuncBodyLengthRule.ts index e276dc24f..a8c254bbb 100644 --- a/src/maxFuncBodyLengthRule.ts +++ b/src/maxFuncBodyLengthRule.ts @@ -40,15 +40,15 @@ const IGNORE_PARAMETERS_TO_FUNCTION = 'ignore-parameters-to-function-regex'; const IGNORE_COMMENTS = 'ignore-comments'; class MaxFunctionBodyLengthRuleWalker extends Lint.RuleWalker { - private maxBodyLength: number; - private maxFuncBodyLength: number; - private maxFuncExpressionBodyLength: number; - private maxArrowBodyLength: number; - private maxMethodBodyLength: number; - private maxCtorBodyLength: number; - private ignoreComments: boolean; - private currentClassName: string; - private ignoreParametersToFunctionRegex: RegExp; + private maxBodyLength!: number; + private maxFuncBodyLength!: number; + private maxFuncExpressionBodyLength!: number; + private maxArrowBodyLength!: number; + private maxMethodBodyLength!: number; + private maxCtorBodyLength!: number; + private ignoreComments!: boolean; + private currentClassName: string | undefined; + private ignoreParametersToFunctionRegex!: RegExp; private ignoreNodes: ts.Node[] = []; constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) { diff --git a/src/mochaNoSideEffectCodeRule.ts b/src/mochaNoSideEffectCodeRule.ts index f0e9e8611..4e58b2f66 100644 --- a/src/mochaNoSideEffectCodeRule.ts +++ b/src/mochaNoSideEffectCodeRule.ts @@ -36,14 +36,14 @@ export class Rule extends Lint.Rules.AbstractRule { class MochaNoSideEffectCodeRuleWalker extends ErrorTolerantWalker { private isInDescribe: boolean = false; - private ignoreRegex: RegExp; + private ignoreRegex!: RegExp; constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) { super(sourceFile, options); this.parseOptions(); } - private parseOptions () { + private parseOptions() { this.getOptions().forEach((opt: any) => { if (typeof(opt) === 'object') { if (opt.ignore != null) { @@ -60,7 +60,9 @@ class MochaNoSideEffectCodeRuleWalker extends ErrorTolerantWalker { if (statement.kind === ts.SyntaxKind.VariableStatement) { const declarationList: ts.VariableDeclarationList = (statement).declarationList; declarationList.declarations.forEach((declaration: ts.VariableDeclaration): void => { - this.validateExpression(declaration.initializer, declaration); + if (declaration.initializer !== undefined) { + this.validateExpression(declaration.initializer, declaration); + } }); } @@ -75,7 +77,7 @@ class MochaNoSideEffectCodeRuleWalker extends ErrorTolerantWalker { } protected visitVariableDeclaration(node: ts.VariableDeclaration): void { - if (this.isInDescribe === true) { + if (this.isInDescribe === true && node.initializer !== undefined) { this.validateExpression(node.initializer, node); } } diff --git a/src/mochaUnneededDoneRule.ts b/src/mochaUnneededDoneRule.ts index d436b1baa..cbf9a766c 100644 --- a/src/mochaUnneededDoneRule.ts +++ b/src/mochaUnneededDoneRule.ts @@ -51,7 +51,7 @@ class MochaUnneededDoneRuleWalker extends ErrorTolerantWalker { } private validateMochaDoneUsage(node: ts.FunctionLikeDeclaration): void { - const doneIdentifier: ts.Identifier = this.maybeGetMochaDoneParameter(node); + const doneIdentifier = this.maybeGetMochaDoneParameter(node); if (doneIdentifier == null) { return; } @@ -86,7 +86,7 @@ class MochaUnneededDoneRuleWalker extends ErrorTolerantWalker { }); } - private maybeGetMochaDoneParameter(node: ts.FunctionLikeDeclaration): ts.Identifier { + private maybeGetMochaDoneParameter(node: ts.FunctionLikeDeclaration): ts.Identifier | null { if (node.parameters.length === 0) { return null; } @@ -107,7 +107,7 @@ class MochaUnneededDoneRuleWalker extends ErrorTolerantWalker { class IdentifierReferenceCountWalker extends ErrorTolerantWalker { private identifierText: string; - private count: number; + private count!: number; constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, identifier: ts.Identifier) { super(sourceFile, options); diff --git a/src/noControlRegexRule.ts b/src/noControlRegexRule.ts index fea6629eb..24b354125 100644 --- a/src/noControlRegexRule.ts +++ b/src/noControlRegexRule.ts @@ -51,14 +51,12 @@ class NoControlRegexRuleWalker extends ErrorTolerantWalker { /* tslint:disable:no-control-regex */ private validateCall(expression: ts.CallExpression | ts.NewExpression): void { - if (expression.expression.getText() === 'RegExp') { - if (expression.arguments.length > 0) { - const arg1: ts.Expression = expression.arguments[0]; - if (arg1.kind === ts.SyntaxKind.StringLiteral) { - const regexpText: string = (arg1).text; - if (/[\x00-\x1f]/.test(regexpText)) { - this.addFailureAt(arg1.getStart(), arg1.getWidth(), Rule.FAILURE_STRING); - } + if (expression.expression.getText() === 'RegExp' && expression.arguments !== undefined && expression.arguments.length > 0) { + const arg1: ts.Expression = expression.arguments[0]; + if (arg1.kind === ts.SyntaxKind.StringLiteral) { + const regexpText: string = (arg1).text; + if (/[\x00-\x1f]/.test(regexpText)) { + this.addFailureAt(arg1.getStart(), arg1.getWidth(), Rule.FAILURE_STRING); } } } diff --git a/src/noDocumentWriteRule.ts b/src/noDocumentWriteRule.ts index 40b42bae1..d0bf1a0fb 100644 --- a/src/noDocumentWriteRule.ts +++ b/src/noDocumentWriteRule.ts @@ -37,7 +37,7 @@ class NoDocumentWriteWalker extends ErrorTolerantWalker { protected visitCallExpression(node: ts.CallExpression) { - const functionTarget: string = AstUtils.getFunctionTarget(node); + const functionTarget = AstUtils.getFunctionTarget(node); if (functionTarget === 'document' || functionTarget === 'window.document') { if (node.arguments.length === 1) { const functionName: string = AstUtils.getFunctionName(node); diff --git a/src/noFunctionConstructorWithStringArgsRule.ts b/src/noFunctionConstructorWithStringArgsRule.ts index 54ef34b3b..76af24c1c 100644 --- a/src/noFunctionConstructorWithStringArgsRule.ts +++ b/src/noFunctionConstructorWithStringArgsRule.ts @@ -39,10 +39,8 @@ class NoFunctionConstructorWithStringArgsWalker extends ErrorTolerantWalker { protected visitNewExpression(node: ts.NewExpression): void { const functionName = AstUtils.getFunctionName(node); - if (functionName === 'Function') { - if (node.arguments.length > 0) { - this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING); - } + if (functionName === 'Function' && node.arguments !== undefined && node.arguments.length > 0) { + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING); } super.visitNewExpression(node); } diff --git a/src/noHttpStringRule.ts b/src/noHttpStringRule.ts index ebbf96538..76e43465a 100644 --- a/src/noHttpStringRule.ts +++ b/src/noHttpStringRule.ts @@ -52,8 +52,8 @@ class NoHttpStringWalker extends ErrorTolerantWalker { } private visitLiteralExpression(node: ts.LiteralExpression | ts.LiteralLikeNode): void { - const stringText : string = node.text; - // tslint:disable no-http-string + const stringText: string = node.text; + // tslint:disable-next-line no-http-string if (stringText.indexOf('http:') === 0) { if (!this.isSuppressed(stringText)) { const failureString = Rule.FAILURE_STRING + '\'' + stringText + '\''; @@ -62,14 +62,14 @@ class NoHttpStringWalker extends ErrorTolerantWalker { } } - private isSuppressed(stringText: string) : boolean { - const allExceptions : string[] = NoHttpStringWalker.getExceptions(this.getOptions()); - return Utils.exists(allExceptions, (exception: string) : boolean => { + private isSuppressed(stringText: string): boolean { + const allExceptions = NoHttpStringWalker.getExceptions(this.getOptions()); + return Utils.exists(allExceptions, (exception: string): boolean => { return new RegExp(exception).test(stringText); }); } - private static getExceptions(options : Lint.IOptions) : string[] { + private static getExceptions(options: Lint.IOptions): string[] | null { if (options.ruleArguments instanceof Array) { return options.ruleArguments[0]; } diff --git a/src/noInnerHtmlRule.ts b/src/noInnerHtmlRule.ts index 6ed050d78..73fc826f7 100644 --- a/src/noInnerHtmlRule.ts +++ b/src/noInnerHtmlRule.ts @@ -68,7 +68,7 @@ class NoInnerHtmlRuleWalker extends ErrorTolerantWalker { if (functionName === 'html') { if (node.arguments.length > 0) { const functionTarget = AstUtils.getFunctionTarget(node); - if (this.htmlLibExpressionRegex.test(functionTarget)) { + if (functionTarget !== null && this.htmlLibExpressionRegex.test(functionTarget)) { this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_HTML_LIB + node.getText()); } } diff --git a/src/noInvalidRegexpRule.ts b/src/noInvalidRegexpRule.ts index 7112f9dfe..7c92870d6 100644 --- a/src/noInvalidRegexpRule.ts +++ b/src/noInvalidRegexpRule.ts @@ -40,17 +40,15 @@ class NoInvalidRegexpRuleWalker extends ErrorTolerantWalker { } private validateCall(expression: ts.CallExpression | ts.NewExpression): void { - if (expression.expression.getText() === 'RegExp') { - if (expression.arguments.length > 0) { - const arg1: ts.Expression = expression.arguments[0]; - if (arg1.kind === ts.SyntaxKind.StringLiteral) { - const regexpText: string = (arg1).text; - try { - // tslint:disable-next-line:no-unused-expression - new RegExp(regexpText); - } catch (e) { - this.addFailureAt(arg1.getStart(), arg1.getWidth(), e.message); - } + if (expression.expression.getText() === 'RegExp' && expression.arguments !== undefined && expression.arguments.length > 0) { + const arg1: ts.Expression = expression.arguments[0]; + if (arg1.kind === ts.SyntaxKind.StringLiteral) { + const regexpText: string = (arg1).text; + try { + // tslint:disable-next-line:no-unused-expression + new RegExp(regexpText); + } catch (e) { + this.addFailureAt(arg1.getStart(), arg1.getWidth(), e.message); } } } diff --git a/src/noJqueryRawElementsRule.ts b/src/noJqueryRawElementsRule.ts index f09e9c37e..2e3c81a7b 100644 --- a/src/noJqueryRawElementsRule.ts +++ b/src/noJqueryRawElementsRule.ts @@ -69,7 +69,7 @@ class NoJqueryRawElementsRuleWalker extends Lint.RuleWalker { return false; } - const match: RegExpMatchArray = text.match(/^<[A-Za-z]+\s*>(.*)<\/[A-Za-z]+\s*>$/m); + const match = text.match(/^<[A-Za-z]+\s*>(.*)<\/[A-Za-z]+\s*>$/m); if (match != null && match[1] != null) { const enclosedContent: string = match[1]; // get the stuff inside the tag if (enclosedContent.indexOf('<') === -1 && enclosedContent.indexOf('>') === -1) { diff --git a/src/noRegexSpacesRule.ts b/src/noRegexSpacesRule.ts index d821c4ef9..628e70b20 100644 --- a/src/noRegexSpacesRule.ts +++ b/src/noRegexSpacesRule.ts @@ -33,7 +33,7 @@ export class Rule extends Lint.Rules.AbstractRule { class NoRegexSpacesRuleWalker extends ErrorTolerantWalker { protected visitRegularExpressionLiteral(node: ts.Node): void { - const match: RegExpExecArray = /( {2,})+?/.exec(node.getText()); + const match = /( {2,})+?/.exec(node.getText()); if (match != null) { const replacement: string = '{' + match[0].length + '}'; this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + replacement); diff --git a/src/noStatelessClassRule.ts b/src/noStatelessClassRule.ts index bb9cd7cf3..31eb66572 100644 --- a/src/noStatelessClassRule.ts +++ b/src/noStatelessClassRule.ts @@ -87,6 +87,10 @@ class NoStatelessClassRuleWalker extends ErrorTolerantWalker { private constructorDeclaresProperty(ctor: ts.ConstructorDeclaration): boolean { return Utils.exists(ctor.parameters, (param: ts.ParameterDeclaration): boolean => { + if (param.modifiers === undefined) { + return false; + } + return AstUtils.hasModifier(param.modifiers, ts.SyntaxKind.PublicKeyword) || AstUtils.hasModifier(param.modifiers, ts.SyntaxKind.PrivateKeyword) || AstUtils.hasModifier(param.modifiers, ts.SyntaxKind.ProtectedKeyword) diff --git a/src/noStringBasedSetImmediateRule.ts b/src/noStringBasedSetImmediateRule.ts index 8018bec28..7ddd44be0 100644 --- a/src/noStringBasedSetImmediateRule.ts +++ b/src/noStringBasedSetImmediateRule.ts @@ -28,7 +28,7 @@ export class Rule extends Lint.Rules.OptionallyTypedRule { return this.applyWithProgram(sourceFile, undefined); } - public applyWithProgram(sourceFile : ts.SourceFile, program : ts.Program): Lint.RuleFailure[] { + public applyWithProgram(sourceFile : ts.SourceFile, program : ts.Program | undefined): Lint.RuleFailure[] { const walker : Lint.RuleWalker = new NoStringParameterToFunctionCallWalker( sourceFile , 'setImmediate', this.getOptions(), program ); diff --git a/src/noStringBasedSetIntervalRule.ts b/src/noStringBasedSetIntervalRule.ts index 6f0596ab5..388c44305 100644 --- a/src/noStringBasedSetIntervalRule.ts +++ b/src/noStringBasedSetIntervalRule.ts @@ -28,7 +28,7 @@ export class Rule extends Lint.Rules.OptionallyTypedRule { return this.applyWithProgram(sourceFile, undefined); } - public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program | undefined): Lint.RuleFailure[] { const walker : Lint.RuleWalker = new NoStringParameterToFunctionCallWalker( sourceFile , 'setInterval', this.getOptions(), program ); diff --git a/src/noStringBasedSetTimeoutRule.ts b/src/noStringBasedSetTimeoutRule.ts index d07f1805b..9f114fc40 100644 --- a/src/noStringBasedSetTimeoutRule.ts +++ b/src/noStringBasedSetTimeoutRule.ts @@ -28,7 +28,7 @@ export class Rule extends Lint.Rules.OptionallyTypedRule { return this.applyWithProgram(sourceFile, undefined); } - public applyWithProgram(sourceFile : ts.SourceFile, program : ts.Program): Lint.RuleFailure[] { + public applyWithProgram(sourceFile : ts.SourceFile, program : ts.Program | undefined): Lint.RuleFailure[] { const walker : Lint.RuleWalker = new NoStringParameterToFunctionCallWalker( sourceFile , 'setTimeout', this.getOptions(), program ); diff --git a/src/noUnexternalizedStringsRule.ts b/src/noUnexternalizedStringsRule.ts index 55fdb03c9..5432e5d0a 100644 --- a/src/noUnexternalizedStringsRule.ts +++ b/src/noUnexternalizedStringsRule.ts @@ -43,7 +43,7 @@ class NoUnexternalizedStringsRuleWalker extends ErrorTolerantWalker { private static SINGLE_QUOTE: string = '\''; private signatures: Map; - private messageIndex: number; + private messageIndex: number | undefined; private ignores: Map; constructor(sourceFile: ts.SourceFile, opt: Lint.IOptions) { @@ -91,7 +91,7 @@ class NoUnexternalizedStringsRuleWalker extends ErrorTolerantWalker { return; } // We have a string that is a direct argument into the localize call. - const messageArg: ts.Expression = callInfo.argIndex === this.messageIndex + const messageArg = callInfo.argIndex === this.messageIndex ? callInfo.callExpression.arguments[this.messageIndex] : null; if (messageArg && messageArg !== node) { @@ -103,7 +103,7 @@ class NoUnexternalizedStringsRuleWalker extends ErrorTolerantWalker { } private findDescribingParent(node: ts.Node): - { callInfo?: { callExpression: ts.CallExpression, argIndex: number }, ignoreUsage?: boolean; } { + { callInfo?: { callExpression: ts.CallExpression, argIndex: number }, ignoreUsage?: boolean; } | null { const kinds = ts.SyntaxKind; while ((node.parent != null)) { const parent: ts.Node = node.parent; diff --git a/src/noUnnecessaryBindRule.ts b/src/noUnnecessaryBindRule.ts index 4fa9f5ca4..8442b0b0a 100644 --- a/src/noUnnecessaryBindRule.ts +++ b/src/noUnnecessaryBindRule.ts @@ -51,8 +51,8 @@ class NoUnnecessaryBindRuleWalker extends ErrorTolerantWalker { analyzers.forEach((analyzer: CallAnalyzer): void => { if (analyzer.canHandle(node)) { - const contextArgument: ts.Expression = analyzer.getContextArgument(node); - const functionArgument: ts.Expression = analyzer.getFunctionArgument(node); + const contextArgument = analyzer.getContextArgument(node); + const functionArgument = analyzer.getFunctionArgument(node); if (contextArgument == null || functionArgument == null) { return; } @@ -71,8 +71,8 @@ class NoUnnecessaryBindRuleWalker extends ErrorTolerantWalker { interface CallAnalyzer { canHandle(node: ts.CallExpression): boolean; - getContextArgument(node: ts.CallExpression): ts.Expression; - getFunctionArgument(node: ts.CallExpression): ts.Expression; + getContextArgument(node: ts.CallExpression): ts.Expression | null; + getFunctionArgument(node: ts.CallExpression): ts.Expression | null; } class TypeScriptFunctionAnalyzer implements CallAnalyzer { @@ -103,7 +103,7 @@ class UnderscoreStaticAnalyzer implements CallAnalyzer { return isUnderscore; } - public getContextArgument(node: ts.CallExpression): ts.Expression { + public getContextArgument(node: ts.CallExpression): ts.Expression | null { const functionName: string = AstUtils.getFunctionName(node); if (Rule.UNDERSCORE_BINARY_FUNCTION_NAMES.indexOf(functionName) !== -1) { return node.arguments[2]; @@ -117,7 +117,7 @@ class UnderscoreStaticAnalyzer implements CallAnalyzer { return null; } - public getFunctionArgument(node: ts.CallExpression): ts.Expression { + public getFunctionArgument(node: ts.CallExpression): ts.Expression | null { const functionName: string = AstUtils.getFunctionName(node); if (Rule.UNDERSCORE_BINARY_FUNCTION_NAMES.indexOf(functionName) !== -1) { return node.arguments[1]; @@ -144,7 +144,7 @@ class UnderscoreInstanceAnalyzer implements CallAnalyzer { return false; } - public getContextArgument(node: ts.CallExpression): ts.Expression { + public getContextArgument(node: ts.CallExpression): ts.Expression | null { const functionName: string = AstUtils.getFunctionName(node); if (Rule.UNDERSCORE_BINARY_FUNCTION_NAMES.indexOf(functionName) !== -1) { return node.arguments[1]; @@ -156,7 +156,7 @@ class UnderscoreInstanceAnalyzer implements CallAnalyzer { return null; } - public getFunctionArgument(node: ts.CallExpression): ts.Expression { + public getFunctionArgument(node: ts.CallExpression): ts.Expression | null { const functionName: string = AstUtils.getFunctionName(node); if (Rule.UNDERSCORE_BINARY_FUNCTION_NAMES.indexOf(functionName) !== -1) { return node.arguments[0]; diff --git a/src/noUnnecessaryFieldInitializationRule.ts b/src/noUnnecessaryFieldInitializationRule.ts index 1e615af33..f68fb7992 100644 --- a/src/noUnnecessaryFieldInitializationRule.ts +++ b/src/noUnnecessaryFieldInitializationRule.ts @@ -35,7 +35,7 @@ export class Rule extends Lint.Rules.AbstractRule { class UnnecessaryFieldInitializationRuleWalker extends ErrorTolerantWalker { - private fieldInitializations: { [index: string]: string } = {}; + private fieldInitializations: { [index: string]: string | undefined } = {}; protected visitClassDeclaration(node: ts.ClassDeclaration): void { this.fieldInitializations = {}; @@ -51,7 +51,7 @@ class UnnecessaryFieldInitializationRuleWalker extends ErrorTolerantWalker { } protected visitPropertyDeclaration(node: ts.PropertyDeclaration): void { - const initializer: ts.Expression = node.initializer; + const initializer = node.initializer; if (node.name.kind === ts.SyntaxKind.Identifier) { const fieldName: string = 'this.' + (node.name).getText(); if (initializer == null) { @@ -60,7 +60,7 @@ class UnnecessaryFieldInitializationRuleWalker extends ErrorTolerantWalker { this.fieldInitializations[fieldName] = initializer.getText(); } } - if (AstUtils.isUndefined(initializer)) { + if (initializer !== undefined && AstUtils.isUndefined(initializer)) { // you should never initialize a field to undefined. const start: number = initializer.getStart(); const width: number = initializer.getWidth(); @@ -84,7 +84,7 @@ class UnnecessaryFieldInitializationRuleWalker extends ErrorTolerantWalker { // field is being assigned to undefined... create error if the field already has that value if (Object.keys(this.fieldInitializations).indexOf(propertyName) > -1) { // make sure the field was declared as undefined - const fieldInitValue: string = this.fieldInitializations[propertyName]; + const fieldInitValue = this.fieldInitializations[propertyName]; if (fieldInitValue == null) { const start: number = property.getStart(); const width: number = property.getWidth(); @@ -93,7 +93,7 @@ class UnnecessaryFieldInitializationRuleWalker extends ErrorTolerantWalker { } } else if (AstUtils.isConstant(binaryExpression.right)) { // field is being assigned a constant... create error if the field already has that value - const fieldInitValue: string = this.fieldInitializations[propertyName]; + const fieldInitValue = this.fieldInitializations[propertyName]; if (fieldInitValue === binaryExpression.right.getText()) { const start: number = binaryExpression.getStart(); const width: number = binaryExpression.getWidth(); diff --git a/src/noUnnecessaryLocalVariableRule.ts b/src/noUnnecessaryLocalVariableRule.ts index 6c4233348..a9886b150 100644 --- a/src/noUnnecessaryLocalVariableRule.ts +++ b/src/noUnnecessaryLocalVariableRule.ts @@ -70,8 +70,8 @@ class UnnecessaryLocalVariableRuleWalker extends ErrorTolerantWalker { const lastStatement = statements[statements.length - 1]; const nextToLastStatement = statements[statements.length - 2]; - const returnedVariableName: string = this.tryToGetReturnedVariableName(lastStatement); - const declaredVariableIdentifier: ts.Identifier | null = this.tryToGetDeclaredVariable(nextToLastStatement); + const returnedVariableName = this.tryToGetReturnedVariableName(lastStatement); + const declaredVariableIdentifier = this.tryToGetDeclaredVariable(nextToLastStatement); if (declaredVariableIdentifier == null) { return; } diff --git a/src/noUnnecessaryOverrideRule.ts b/src/noUnnecessaryOverrideRule.ts index d6bb06d0c..ef0d2ae8c 100644 --- a/src/noUnnecessaryOverrideRule.ts +++ b/src/noUnnecessaryOverrideRule.ts @@ -34,7 +34,7 @@ export class Rule extends Lint.Rules.AbstractRule { class NoUnnecessaryOverrideRuleWalker extends ErrorTolerantWalker { protected visitMethodDeclaration(node: ts.MethodDeclaration): void { if (node.body != null) { - const statement: ts.Statement = this.getSingleStatement(node.body); + const statement = this.getSingleStatement(node.body); if (statement != null) { if (this.isSuperCall(node, statement) && this.isMatchingArgumentList(node, statement)) { this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING + this.getMethodName(node)); @@ -44,7 +44,7 @@ class NoUnnecessaryOverrideRuleWalker extends ErrorTolerantWalker { super.visitMethodDeclaration(node); } - private getSingleStatement(block: ts.Block): ts.Statement { + private getSingleStatement(block: ts.Block): ts.Statement | null { if (block.statements.length === 1) { return block.statements[0]; } @@ -52,7 +52,7 @@ class NoUnnecessaryOverrideRuleWalker extends ErrorTolerantWalker { } private isMatchingArgumentList(node: ts.MethodDeclaration, statement: ts.Statement): boolean { - const call: ts.CallExpression = this.getCallExpressionFromStatement(statement); + const call = this.getCallExpressionFromStatement(statement); if (call == null) { return false; } @@ -86,7 +86,7 @@ class NoUnnecessaryOverrideRuleWalker extends ErrorTolerantWalker { } private isSuperCall(node: ts.MethodDeclaration, statement: ts.Statement): boolean { - const call: ts.CallExpression = this.getCallExpressionFromStatement(statement); + const call = this.getCallExpressionFromStatement(statement); if (call == null) { return false; } @@ -104,8 +104,8 @@ class NoUnnecessaryOverrideRuleWalker extends ErrorTolerantWalker { return methodName === declaredMethodName; } - private getCallExpressionFromStatement(statement: ts.Statement): ts.CallExpression { - let expression: ts.Expression; + private getCallExpressionFromStatement(statement: ts.Statement): ts.CallExpression | null { + let expression: ts.Expression | undefined; if (statement.kind === ts.SyntaxKind.ExpressionStatement) { expression = (statement).expression; } else if (statement.kind === ts.SyntaxKind.ReturnStatement) { diff --git a/src/noUnsupportedBrowserCodeRule.ts b/src/noUnsupportedBrowserCodeRule.ts index 4e3c66baf..05b6b9113 100644 --- a/src/noUnsupportedBrowserCodeRule.ts +++ b/src/noUnsupportedBrowserCodeRule.ts @@ -77,7 +77,7 @@ class NoUnsupportedBrowserCodeRuleWalker extends Lint.RuleWalker { // #2 looks for an optional comparison operator (>=, <=, etc) // #3 looks for a version number const regex = /([a-zA-Z ]*)(>=|<=|<|>)?\s*(\d*)/i; - const match = browser.match(regex); + const match = browser.match(regex)!; return { name: match[1].trim(), diff --git a/src/promiseMustCompleteRule.ts b/src/promiseMustCompleteRule.ts index 02e43838d..726e50562 100644 --- a/src/promiseMustCompleteRule.ts +++ b/src/promiseMustCompleteRule.ts @@ -64,11 +64,14 @@ class PromiseAnalyzer extends ErrorTolerantWalker { } protected visitNewExpression(node: ts.NewExpression): void { - if (this.isPromiseDeclaration(node)) { + if (this.isPromiseDeclaration(node) && node.arguments !== undefined) { const functionArgument: ts.FunctionLikeDeclaration = node.arguments[0]; const functionBody = functionArgument.body; - const competionIdentifiers : ts.Identifier[] = this.getCompletionIdentifiers(functionArgument); - this.validatePromiseUsage(node, functionBody, competionIdentifiers); + + if (functionBody !== undefined) { + const competionIdentifiers : ts.Identifier[] = this.getCompletionIdentifiers(functionArgument); + this.validatePromiseUsage(node, functionBody, competionIdentifiers); + } } super.visitNewExpression(node); } diff --git a/src/reactA11yAnchorsRule.ts b/src/reactA11yAnchorsRule.ts index 073fd05e4..b9e1966cc 100644 --- a/src/reactA11yAnchorsRule.ts +++ b/src/reactA11yAnchorsRule.ts @@ -58,15 +58,15 @@ export class Rule extends Lint.Rules.AbstractRule { class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { - private anchorInfoList: AnchorInfo[] = []; + private anchorInfoList: IAnchorInfo[] = []; public validateAllAnchors(): void { - const sameHrefDifferentTexts: AnchorInfo[] = []; - const differentHrefSameText: AnchorInfo[] = []; + const sameHrefDifferentTexts: IAnchorInfo[] = []; + const differentHrefSameText: IAnchorInfo[] = []; while (this.anchorInfoList.length > 0) { - const current: AnchorInfo = this.anchorInfoList.shift(); - this.anchorInfoList.forEach((anchorInfo: AnchorInfo): void => { + const current: IAnchorInfo = this.anchorInfoList.shift()!; + this.anchorInfoList.forEach((anchorInfo: IAnchorInfo): void => { if (current.href && current.href === anchorInfo.href && (current.text !== anchorInfo.text || current.altText !== anchorInfo.altText) && @@ -92,7 +92,7 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { } } - private firstPosition(anchorInfo: AnchorInfo): string { + private firstPosition(anchorInfo: IAnchorInfo): string { const startPosition: ts.LineAndCharacter = this.createFailure(anchorInfo.start, anchorInfo.width, '').getStartPosition().getLineAndCharacter(); @@ -116,8 +116,8 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { private validateAnchor(parent: ts.Node, openingElement: ts.JsxOpeningLikeElement): void { if (openingElement.tagName.getText() === 'a') { - const anchorInfo: AnchorInfo = { - href: this.getAttribute(openingElement, 'href'), + const anchorInfo: IAnchorInfo = { + href: this.getAttribute(openingElement, 'href') || '', text: this.anchorText(parent), altText: this.imageAlt(parent), start: parent.getStart(), @@ -147,7 +147,7 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { } } - private getAttribute(openingElement: ts.JsxOpeningLikeElement, attributeName: string): string { + private getAttribute(openingElement: ts.JsxOpeningLikeElement, attributeName: string): string | undefined { const attributes: { [propName: string]: ts.JsxAttribute } = getJsxAttributesFromJsxElement(openingElement); const attribute: ts.JsxAttribute = attributes[attributeName]; return attribute ? getStringLiteral(attribute) : ''; @@ -156,9 +156,11 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { /** * Return a string which contains literal text and text in 'alt' attribute. */ - private anchorText(root: ts.Node): string { + private anchorText(root: ts.Node | undefined): string { let title: string = ''; - if (root.kind === ts.SyntaxKind.JsxElement) { + if (root === undefined) { + return title; + } else if (root.kind === ts.SyntaxKind.JsxElement) { const jsxElement: ts.JsxElement = root; jsxElement.children.forEach((child: ts.JsxChild): void => { title += this.anchorText(child); @@ -179,7 +181,7 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { return title; } - private anchorRole(root: ts.Node): string { + private anchorRole(root: ts.Node): string | undefined { const attributesInElement: { [propName: string]: ts.JsxAttribute } = getJsxAttributesFromJsxElement(root); const roleProp: ts.JsxAttribute = attributesInElement[ROLE_STRING]; @@ -189,7 +191,7 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { private imageAltAttribute(openingElement: ts.JsxOpeningLikeElement): string { if (openingElement.tagName.getText() === 'img') { - const altAttribute: string = this.getAttribute(openingElement, 'alt'); + const altAttribute = this.getAttribute(openingElement, 'alt'); return altAttribute === undefined ? '' : altAttribute; } @@ -216,10 +218,10 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { } } -class AnchorInfo { - public href: string = ''; - public text: string = ''; - public altText: string = ''; - public start: number = 0; - public width: number = 0; -} \ No newline at end of file +interface IAnchorInfo { + href: string; + text: string; + altText: string; + start: number; + width: number; +} diff --git a/src/reactA11yAriaUnsupportedElementsRule.ts b/src/reactA11yAriaUnsupportedElementsRule.ts index bd48655a4..2e5161d7b 100644 --- a/src/reactA11yAriaUnsupportedElementsRule.ts +++ b/src/reactA11yAriaUnsupportedElementsRule.ts @@ -55,12 +55,12 @@ class ReactA11yAriaUnsupportedElementsWalker extends Lint.RuleWalker { private validateOpeningElement(node: ts.JsxOpeningLikeElement): void { const tagName: string = node.tagName.getText(); - if (!DOM_SCHEMA[tagName]) { + if (!(DOM_SCHEMA)[tagName]) { return; } - const supportAria: boolean = DOM_SCHEMA[tagName].supportAria != null - ? DOM_SCHEMA[tagName].supportAria + const supportAria: boolean = (DOM_SCHEMA)[tagName].supportAria != null + ? (DOM_SCHEMA)[tagName].supportAria : false; if (supportAria) { diff --git a/src/reactA11yEventHasRoleRule.ts b/src/reactA11yEventHasRoleRule.ts index 8f3e13197..a90cf2f4e 100644 --- a/src/reactA11yEventHasRoleRule.ts +++ b/src/reactA11yEventHasRoleRule.ts @@ -52,7 +52,7 @@ class ReactA11yEventHasRoleWalker extends Lint.RuleWalker { private checkJsxOpeningElement(node: ts.JsxOpeningLikeElement): void { const tagName: string = node.tagName.getText(); - if (!DOM_SCHEMA[tagName]) { + if (!(DOM_SCHEMA)[tagName]) { return; } diff --git a/src/reactA11yImageButtonHasAltRule.ts b/src/reactA11yImageButtonHasAltRule.ts index b073953a1..c5c9ff978 100644 --- a/src/reactA11yImageButtonHasAltRule.ts +++ b/src/reactA11yImageButtonHasAltRule.ts @@ -56,7 +56,10 @@ class ReactA11yImageButtonHasAltWalker extends Lint.RuleWalker { const attributes: { [propName: string]: ts.JsxAttribute } = getJsxAttributesFromJsxElement(node); const typeAttribute: ts.JsxAttribute = attributes[TYPE_STRING]; - if (!typeAttribute || !isStringLiteral(typeAttribute.initializer) || getStringLiteral(typeAttribute).toLowerCase() !== 'image') { + if (!typeAttribute + || typeAttribute.initializer === undefined + || !isStringLiteral(typeAttribute.initializer) + || getStringLiteral(typeAttribute)!.toLowerCase() !== 'image') { return; } diff --git a/src/reactA11yImgHasAltRule.ts b/src/reactA11yImgHasAltRule.ts index e0fbb95b9..7cd5598bc 100644 --- a/src/reactA11yImgHasAltRule.ts +++ b/src/reactA11yImgHasAltRule.ts @@ -84,7 +84,8 @@ class ImgHasAltWalker extends Lint.RuleWalker { } // If element contains JsxSpreadElement in which there could possibly be alt attribute, don't check it. - if (getAllAttributesFromJsxElement(node).some(isJsxSpreadAttribute)) { + const nodeAttributes = getAllAttributesFromJsxElement(node); + if (nodeAttributes !== null && nodeAttributes.some(isJsxSpreadAttribute)) { return; } @@ -99,7 +100,7 @@ class ImgHasAltWalker extends Lint.RuleWalker { ); } else { const roleAttribute: ts.JsxAttribute = attributes[ROLE_STRING]; - const roleAttributeValue: string = roleAttribute ? getStringLiteral(roleAttribute) : ''; + const roleAttributeValue = roleAttribute ? getStringLiteral(roleAttribute) : ''; const isPresentationRole: boolean = !!String(roleAttributeValue).toLowerCase().match(/\bpresentation\b/); const isEmptyAlt: boolean = isEmpty(altAttribute) || getStringLiteral(altAttribute) === ''; const allowNonEmptyAltWithRolePresentation: boolean = options.length > 1 diff --git a/src/reactA11yLangRule.ts b/src/reactA11yLangRule.ts index 238d991b5..71cfa514b 100644 --- a/src/reactA11yLangRule.ts +++ b/src/reactA11yLangRule.ts @@ -68,9 +68,9 @@ class ReactA11yLangRuleWalker extends ErrorTolerantWalker { attributes.properties.forEach((attribute: ts.JsxAttributeLike): void => { if (attribute.kind === ts.SyntaxKind.JsxAttribute) { - if ((attribute).name.getText() === 'lang') { + if (attribute.name.getText() === 'lang') { langFound = true; - if ((attribute).initializer.kind === ts.SyntaxKind.StringLiteral) { + if (attribute.initializer!.kind === ts.SyntaxKind.StringLiteral) { const langText: string = ((attribute).initializer).text; if ((LANGUAGE_CODES.indexOf(langText)) === -1) { this.addFailureAt( diff --git a/src/reactA11yMetaRule.ts b/src/reactA11yMetaRule.ts index abf3384a5..f82bc61ab 100644 --- a/src/reactA11yMetaRule.ts +++ b/src/reactA11yMetaRule.ts @@ -52,7 +52,7 @@ class ReactA11yMetaRuleWalker extends ErrorTolerantWalker { if (parameter.kind === ts.SyntaxKind.JsxAttribute) { const attribute: ts.JsxAttribute = parameter; if (attribute.name.getText() === 'http-equiv') { - if (this.isStringLiteral(attribute.initializer, 'refresh')) { + if (attribute.initializer !== undefined && this.isStringLiteral(attribute.initializer, 'refresh')) { this.addFailureAt(parent.getStart(), openElement.getWidth(), FAILURE_STRING); } } @@ -61,14 +61,14 @@ class ReactA11yMetaRuleWalker extends ErrorTolerantWalker { } } - private isStringLiteral(expression: ts.Expression, literal: string): boolean { + private isStringLiteral(expression: ts.Expression, literal: string): boolean | null { if (expression != null) { if (expression.kind === ts.SyntaxKind.StringLiteral) { const value: string = (expression).text; return value === literal; } else if (expression.kind === ts.SyntaxKind.JsxExpression) { const exp: ts.JsxExpression = expression; - if (exp.expression.kind === ts.SyntaxKind.StringLiteral) { + if (exp.expression !== undefined && exp.expression.kind === ts.SyntaxKind.StringLiteral) { const value: string = (exp.expression).text; return value === literal; } diff --git a/src/reactA11yPropsRule.ts b/src/reactA11yPropsRule.ts index 2531d645e..dbe0bc32b 100644 --- a/src/reactA11yPropsRule.ts +++ b/src/reactA11yPropsRule.ts @@ -42,8 +42,7 @@ export class Rule extends Lint.Rules.AbstractRule { class A11yPropsWalker extends Lint.RuleWalker { public visitJsxAttribute(node: ts.JsxAttribute): void { - const name: string = getPropName(node); - + const name = getPropName(node); if (!name || !name.match(/^aria-/i)) { return; } diff --git a/src/reactA11yProptypesRule.ts b/src/reactA11yProptypesRule.ts index 9b6fa2560..4eee63f19 100644 --- a/src/reactA11yProptypesRule.ts +++ b/src/reactA11yProptypesRule.ts @@ -66,7 +66,12 @@ export class Rule extends Lint.Rules.AbstractRule { class ReactA11yProptypesWalker extends Lint.RuleWalker { public visitJsxAttribute(node: ts.JsxAttribute): void { - const propName: string = getPropName(node).toLowerCase(); + const propNameNode = getPropName(node); + if (propNameNode === undefined) { + return; + } + + const propName = propNameNode.toLowerCase(); // If there is no aria-* attribute, skip it. if (!aria[propName]) { @@ -101,11 +106,15 @@ class ReactA11yProptypesWalker extends Lint.RuleWalker { } private validityCheck( - propValueExpression: ts.Expression, + propValueExpression: ts.Expression | null | undefined, propValue: string, expectedType: string, permittedValues: string[] ): boolean { + if (propValueExpression == null) { + return true; + } + switch (expectedType) { case 'boolean': return this.isBoolean(propValueExpression); case 'tristate': return this.isBoolean(propValueExpression) || this.isMixed(propValueExpression); @@ -123,11 +132,11 @@ class ReactA11yProptypesWalker extends Lint.RuleWalker { } } - private isUndefined(node: ts.Expression): boolean { + private isUndefined(node: ts.Expression | null | undefined): boolean { if (!node) { return true; } else if (isJsxExpression(node)) { - const expression: ts.Expression = node.expression; + const expression = node.expression; if (!expression) { return true; } else if (AstUtils.isUndefined(expression)) { @@ -144,8 +153,8 @@ class ReactA11yProptypesWalker extends Lint.RuleWalker { * For this case
* we can't check the type of atrribute's expression until running time. */ - private isComplexType(node: ts.Expression): boolean { - return !this.isUndefined(node) && isJsxExpression(node) && !AstUtils.isConstant(node.expression); + private isComplexType(node: ts.Expression | null | undefined): boolean { + return node != null && !this.isUndefined(node) && isJsxExpression(node) && !AstUtils.isConstant(node.expression); } private isBoolean(node: ts.Expression): boolean { @@ -154,7 +163,10 @@ class ReactA11yProptypesWalker extends Lint.RuleWalker { return propValue === 'true' || propValue === 'false'; } else if (isJsxExpression(node)) { - const expression: ts.Expression = node.expression; + const expression = node.expression; + if (expression === undefined) { + return false; + } if (isStringLiteral(expression)) { const propValue: string = expression.text.toLowerCase(); @@ -172,7 +184,10 @@ class ReactA11yProptypesWalker extends Lint.RuleWalker { if (isStringLiteral(node)) { return node.text.toLowerCase() === 'mixed'; } else if (isJsxExpression(node)) { - const expression: ts.Expression = node.expression; + const expression = node.expression; + if (expression === undefined) { + return false; + } return isStringLiteral(expression) && expression.text.toLowerCase() === 'mixed'; } @@ -184,7 +199,10 @@ class ReactA11yProptypesWalker extends Lint.RuleWalker { if (isStringLiteral(node)) { return !isNaN(Number(node.text)); } else if (isJsxExpression(node)) { - const expression: ts.Expression = node.expression; + const expression = node.expression; + if (expression === undefined) { + return false; + } if (isStringLiteral(expression)) { return !isNaN(Number(expression.text)); @@ -202,7 +220,10 @@ class ReactA11yProptypesWalker extends Lint.RuleWalker { return !isNaN(value) && Math.round(value) === value; } else if (isJsxExpression(node)) { - const expression: ts.Expression = node.expression; + const expression = node.expression; + if (expression === undefined) { + return false; + } if (isStringLiteral(expression)) { const value: number = Number(expression.text); @@ -221,6 +242,6 @@ class ReactA11yProptypesWalker extends Lint.RuleWalker { } private isString(node: ts.Expression): boolean { - return isStringLiteral(node) || (isJsxExpression(node) && isStringLiteral(node.expression)); + return isStringLiteral(node) || (isJsxExpression(node) && node.expression !== undefined && isStringLiteral(node.expression)); } } diff --git a/src/reactA11yRoleHasRequiredAriaPropsRule.ts b/src/reactA11yRoleHasRequiredAriaPropsRule.ts index d83b68108..e6346b684 100644 --- a/src/reactA11yRoleHasRequiredAriaPropsRule.ts +++ b/src/reactA11yRoleHasRequiredAriaPropsRule.ts @@ -79,10 +79,10 @@ class A11yRoleHasRequiredAriaPropsWalker extends Lint.RuleWalker { const roleProp: ts.JsxAttribute = attributesInElement[ROLE_STRING]; // If role attribute is specified, get the role value. Otherwise get the implicit role from tag name. - const roleValue: string = roleProp ? getStringLiteral(roleProp) : getImplicitRole(node); + const roleValue = roleProp ? getStringLiteral(roleProp) : getImplicitRole(node); const isImplicitRole: boolean = !roleProp && !!roleValue; const normalizedRoles: string[] = (roleValue || '').toLowerCase().split(' ') - .filter((role: string) => !!ROLES[role]); + .filter((role: string) => !!(ROLES)[role]); if (normalizedRoles.length === 0) { return; @@ -91,7 +91,7 @@ class A11yRoleHasRequiredAriaPropsWalker extends Lint.RuleWalker { let requiredAttributeNames: string[] = []; normalizedRoles.forEach((role: string) => { - requiredAttributeNames = requiredAttributeNames.concat(ROLES[role].requiredProps || []); + requiredAttributeNames = requiredAttributeNames.concat((ROLES)[role].requiredProps || []); }); const attributeNamesInElement: string[] = Object.keys(attributesInElement) diff --git a/src/reactA11yRoleRule.ts b/src/reactA11yRoleRule.ts index a3a616188..f999ab309 100644 --- a/src/reactA11yRoleRule.ts +++ b/src/reactA11yRoleRule.ts @@ -14,7 +14,7 @@ const ROLE_SCHEMA: IRoleSchema = require('./utils/attributes/roleSchema.json'); const ROLES: IRole[] = ROLE_SCHEMA.roles; // The array of non-abstract valid rules. -const VALID_ROLES: string[] = Object.keys(ROLES).filter(role => ROLES[role].isAbstract === false); +const VALID_ROLES: string[] = Object.keys(ROLES).filter(role => (ROLES)[role].isAbstract === false); export function getFailureStringUndefinedRole(): string { return '\'role\' attribute empty. Either select a role from https://www.w3.org/TR/wai-aria/roles#role_definitions, ' + @@ -51,20 +51,20 @@ export class Rule extends Lint.Rules.AbstractRule { class A11yRoleRuleWalker extends Lint.RuleWalker { public visitJsxAttribute(node: ts.JsxAttribute): void { - const name: string = getPropName(node); + const name = getPropName(node); if (!name || name.toLowerCase() !== 'role') { return; } - const roleValue: string = getStringLiteral(node); + const roleValue = getStringLiteral(node); if (roleValue) { // Splitted by space doesn't mean the multiple role definition is correct, // just because this rule is not checking if it is using multiple role definition. const normalizedValues: string[] = roleValue.toLowerCase().split(' '); - if (normalizedValues.some(value => value && VALID_ROLES.indexOf(value) === -1)) { + if (normalizedValues.some(value => !!(value && VALID_ROLES.indexOf(value) === -1))) { this.addFailureAt(node.getStart(), node.getWidth(), getFailureStringInvalidRole(roleValue)); } } else if (roleValue === '' || isEmpty(node)) { diff --git a/src/reactA11yRoleSupportsAriaPropsRule.ts b/src/reactA11yRoleSupportsAriaPropsRule.ts index e36806ce9..5e81cf989 100644 --- a/src/reactA11yRoleSupportsAriaPropsRule.ts +++ b/src/reactA11yRoleSupportsAriaPropsRule.ts @@ -72,7 +72,7 @@ class A11yRoleSupportsAriaPropsWalker extends Lint.RuleWalker { private checkJsxElement(node: ts.JsxOpeningLikeElement): void { const attributesInElement: { [propName: string]: ts.JsxAttribute } = getJsxAttributesFromJsxElement(node); const roleProp: ts.JsxAttribute = attributesInElement[ROLE_STRING]; - let roleValue: string; + let roleValue: string | undefined; // Match react custom element whose tag name starts with uppercase character. if (node.tagName.getText().match(/^[A-Z].*/)) { @@ -88,13 +88,13 @@ class A11yRoleSupportsAriaPropsWalker extends Lint.RuleWalker { } const isImplicitRole: boolean = !roleProp && !!roleValue; - const normalizedRoles: string[] = (roleValue || '').toLowerCase().split(' ') - .filter((role: string) => !!ROLES[role]); + const normalizedRoles = (roleValue || '').toLowerCase().split(' ') + .filter((role: string) => role in ROLES); let supportedAttributeNames: string[] = ROLE_SCHEMA.globalSupportedProps; - normalizedRoles.forEach((role: string) => { - supportedAttributeNames = supportedAttributeNames.concat(ROLES[role].additionalSupportedProps || []); + normalizedRoles.forEach((role) => { + supportedAttributeNames = supportedAttributeNames.concat((ROLES)[role].additionalSupportedProps || []); }); const attributeNamesInElement: string[] = Object.keys(attributesInElement) diff --git a/src/reactA11yTabindexNoPositiveRule.ts b/src/reactA11yTabindexNoPositiveRule.ts index 536c95f51..9f090806b 100644 --- a/src/reactA11yTabindexNoPositiveRule.ts +++ b/src/reactA11yTabindexNoPositiveRule.ts @@ -36,13 +36,13 @@ export class Rule extends Lint.Rules.AbstractRule { class A11yTabindexNoPositiveWalker extends Lint.RuleWalker { public visitJsxAttribute(node: ts.JsxAttribute): void { - const name: string = getPropName(node); + const name = getPropName(node); if (!name || name.toLowerCase() !== 'tabindex') { return; } - const literalString: string = getNumericLiteral(node) || getStringLiteral(node); + const literalString = getNumericLiteral(node) || getStringLiteral(node); // In case the attribute has no value of empty value. if (literalString === '') { diff --git a/src/reactA11yTitlesRule.ts b/src/reactA11yTitlesRule.ts index 98d996d63..15180b896 100644 --- a/src/reactA11yTitlesRule.ts +++ b/src/reactA11yTitlesRule.ts @@ -60,7 +60,7 @@ class ReactA11yTitlesRuleWalker extends ErrorTolerantWalker { this.validateTitleText(value.getText(), node); } else if (node.children[0].kind === ts.SyntaxKind.JsxExpression) { const exp: ts.JsxExpression = node.children[0]; - if (exp.expression.kind === ts.SyntaxKind.StringLiteral) { + if (exp.expression !== undefined && exp.expression.kind === ts.SyntaxKind.StringLiteral) { this.validateTitleText((exp.expression).text, node); } } diff --git a/src/reactAnchorBlankNoopenerRule.ts b/src/reactAnchorBlankNoopenerRule.ts index 7fbb4bf91..2129cb27b 100644 --- a/src/reactAnchorBlankNoopenerRule.ts +++ b/src/reactAnchorBlankNoopenerRule.ts @@ -73,7 +73,7 @@ function isRelAttributeValue(attribute: ts.JsxAttribute): boolean { return false; } - if (attribute.initializer.kind === ts.SyntaxKind.JsxExpression) { + if (attribute.initializer !== undefined && attribute.initializer.kind === ts.SyntaxKind.JsxExpression) { const expression: ts.JsxExpression = attribute.initializer; if (expression.expression != null && expression.expression.kind !== ts.SyntaxKind.StringLiteral) { return true; // attribute value is not a string literal, so do not validate diff --git a/src/reactNoDangerousHtmlRule.ts b/src/reactNoDangerousHtmlRule.ts index 82837fb7c..25890d1b4 100644 --- a/src/reactNoDangerousHtmlRule.ts +++ b/src/reactNoDangerousHtmlRule.ts @@ -38,7 +38,7 @@ export class Rule extends Lint.Rules.AbstractRule { * Exposed for testing. */ /* tslint:disable:function-name */ - public static getExceptions(options : Lint.IOptions) : Exception[] { + public static getExceptions(options : Lint.IOptions): Exception[] | null { /* tslint:enable:function-name */ if (options.ruleArguments instanceof Array) { return options.ruleArguments[0]; @@ -111,7 +111,7 @@ class NoDangerousHtmlWalker extends ErrorTolerantWalker { } private isSuppressed(methodName : string): boolean { - const exceptions : Exception[] = Rule.getExceptions(this.getOptions()); + const exceptions = Rule.getExceptions(this.getOptions()); if (exceptions == null || exceptions.length === 0) { return false; // no file specified means the usage is not suppressed } diff --git a/src/reactThisBindingIssueRule.ts b/src/reactThisBindingIssueRule.ts index 868375440..c444fc6ba 100644 --- a/src/reactThisBindingIssueRule.ts +++ b/src/reactThisBindingIssueRule.ts @@ -46,7 +46,7 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { private allowAnonymousListeners: boolean = false; private boundListeners: string[] = []; private declaredMethods: string[] = []; - private scope: Scope; + private scope: Scope | null = null; constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) { super(sourceFile, options); @@ -128,10 +128,10 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { node.attributes.properties.forEach((attributeLikeElement: ts.JsxAttribute | ts.JsxSpreadAttribute): void => { if (this.isUnboundListener(attributeLikeElement)) { const attribute: ts.JsxAttribute = attributeLikeElement; - if (attribute.initializer.kind === ts.SyntaxKind.StringLiteral) { + const jsxExpression = attribute.initializer; + if (jsxExpression === undefined || jsxExpression.kind === ts.SyntaxKind.StringLiteral) { return; } - const jsxExpression: ts.JsxExpression = attribute.initializer; const propAccess: ts.PropertyAccessExpression = jsxExpression.expression; const listenerText: string = propAccess.getText(); if (this.declaredMethods.indexOf(listenerText) > -1 && this.boundListeners.indexOf(listenerText) === -1) { @@ -142,11 +142,16 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { } } else if (this.isAttributeAnonymousFunction(attributeLikeElement)) { const attribute: ts.JsxAttribute = attributeLikeElement; - if (attribute.initializer.kind === ts.SyntaxKind.StringLiteral) { + const jsxExpression = attribute.initializer; + if (jsxExpression === undefined || jsxExpression.kind === ts.SyntaxKind.StringLiteral) { return; } - const jsxExpression: ts.JsxExpression = attribute.initializer; - const expression: ts.Expression = jsxExpression.expression; + + const expression = jsxExpression.expression; + if (expression === undefined) { + return; + } + const start: number = expression.getStart(); const widget: number = expression.getWidth(); const message: string = FAILURE_ANONYMOUS_LISTENER + Utils.trimTo(expression.getText(), 30); @@ -162,15 +167,13 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { if (attributeLikeElement.kind === ts.SyntaxKind.JsxAttribute) { const attribute: ts.JsxAttribute = attributeLikeElement; if (attribute.initializer != null && attribute.initializer.kind === ts.SyntaxKind.JsxExpression) { - const jsxExpression: ts.JsxExpression = attribute.initializer; - const expression: ts.Expression = jsxExpression.expression; - return this.isExpressionAnonymousFunction(expression); + return this.isExpressionAnonymousFunction(attribute.initializer.expression); } } return false; } - private isExpressionAnonymousFunction(expression: ts.Expression): boolean { + private isExpressionAnonymousFunction(expression: ts.Expression | null | undefined): boolean { if (expression == null) { return false; } @@ -188,7 +191,7 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { return true; // bind functions on Function or _ create a new anonymous instance of a function } } - if (expression.kind === ts.SyntaxKind.Identifier) { + if (expression.kind === ts.SyntaxKind.Identifier && this.scope != null) { const symbolText: string = expression.getText(); return this.scope.isFunctionSymbol(symbolText); } @@ -199,7 +202,7 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { if (attributeLikeElement.kind === ts.SyntaxKind.JsxAttribute) { const attribute: ts.JsxAttribute = attributeLikeElement; if (attribute.initializer != null && attribute.initializer.kind === ts.SyntaxKind.JsxExpression) { - const jsxExpression: ts.JsxExpression = attribute.initializer; + const jsxExpression = attribute.initializer; if (jsxExpression.expression != null && jsxExpression.expression.kind === ts.SyntaxKind.PropertyAccessExpression) { const propAccess: ts.PropertyAccessExpression = jsxExpression.expression; if (propAccess.expression.getText() === 'this') { @@ -221,7 +224,7 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { if (node.body != null && node.body.statements != null) { node.body.statements.forEach((statement: ts.Statement): void => { if (statement.kind === ts.SyntaxKind.ExpressionStatement) { - const expressionStatement: ts.ExpressionStatement = statement; + const expressionStatement = statement; const expression = expressionStatement.expression; if (expression.kind === ts.SyntaxKind.BinaryExpression) { const binaryExpression: ts.BinaryExpression = expression; @@ -238,7 +241,7 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { && callExpression.arguments.length === 1 && callExpression.arguments[0].getText() === 'this') { - const rightPropText: string = AstUtils.getFunctionTarget(callExpression); + const rightPropText = AstUtils.getFunctionTarget(callExpression); if (leftPropText === rightPropText) { if (result.indexOf(rightPropText) === -1) { result.push(rightPropText); diff --git a/src/reactTsxCurlySpacingRule.ts b/src/reactTsxCurlySpacingRule.ts index aec7af760..d6d48fb1e 100644 --- a/src/reactTsxCurlySpacingRule.ts +++ b/src/reactTsxCurlySpacingRule.ts @@ -52,8 +52,8 @@ class TsxCurlySpacingWalker extends Lint.RuleWalker { public visitJsxExpression(node: ts.JsxExpression): void { const childrenCount: number = node.getChildCount(); - const first: ts.Node = node.getFirstToken(); // '{' sign - const last: ts.Node = node.getLastToken(); // '}' sign + const first = node.getFirstToken(); // '{' sign + const last = node.getLastToken(); // '}' sign const second: ts.Node = node.getChildAt(1); // after '{' sign const penultimate: ts.Node = node.getChildAt(childrenCount - 2); // before '}' sign this.validateBraceSpacing(node, first, second, first); @@ -71,7 +71,15 @@ class TsxCurlySpacingWalker extends Lint.RuleWalker { } } - private validateBraceSpacing(node: ts.Node, first: ts.Node, second: ts.Node, violationRoot: ts.Node): void { + private validateBraceSpacing( + node: ts.Node, + first: ts.Node | undefined, + second: ts.Node | undefined, + violationRoot: ts.Node | undefined + ): void { + if (first === undefined || second === undefined || violationRoot === undefined) { + return; + } if (this.isMultiline(first, second)) { if (!this.allowMultiline) { diff --git a/src/reactUnusedPropsAndStateRule.ts b/src/reactUnusedPropsAndStateRule.ts index 6b02431e7..487db6063 100644 --- a/src/reactUnusedPropsAndStateRule.ts +++ b/src/reactUnusedPropsAndStateRule.ts @@ -47,8 +47,8 @@ class ReactUnusedPropsAndStateRuleWalker extends ErrorTolerantWalker { private stateNames: string[] = []; private stateNodes: { [index: string]: ts.TypeElement } = {}; private classDeclarations: ts.ClassDeclaration[] = []; - private propsAlias: string; - private stateAlias: string; + private propsAlias: string | undefined; + private stateAlias: string | undefined; private propsInterfaceRegex: RegExp = /^Props$/; private stateInterfaceRegex: RegExp = /^State$/; diff --git a/src/tests/FixNoRequireImportsFormatterTests.ts b/src/tests/FixNoRequireImportsFormatterTests.ts index a92648279..2af61b324 100644 --- a/src/tests/FixNoRequireImportsFormatterTests.ts +++ b/src/tests/FixNoRequireImportsFormatterTests.ts @@ -5,7 +5,7 @@ import {Formatter} from '../fixNoRequireImportsFormatter'; class FormatterForTesting extends Formatter { private input: string; - private output: string; + private output!: string; constructor(input: string) { super(); diff --git a/src/tests/FixNoVarKeywordFormatterTests.ts b/src/tests/FixNoVarKeywordFormatterTests.ts index 059f25593..810b0a850 100644 --- a/src/tests/FixNoVarKeywordFormatterTests.ts +++ b/src/tests/FixNoVarKeywordFormatterTests.ts @@ -2,11 +2,10 @@ import * as chai from 'chai'; import {TestHelper} from './TestHelper'; import {Formatter} from '../fixNoVarKeywordFormatter'; -/* tslint:disable:no-consecutive-blank-lines */ class FixNoVarKeywordFormatterForTesting extends Formatter { private input: string; - private output: string; + private output!: string; constructor(input: string) { super(); @@ -91,4 +90,3 @@ let foo = bar; chai.expect(formatter.getOutput()).to.equal(`\r\n let foo = bar;\r\n`); }); }); -/* tslint:enable:no-consecutive-blank-lines */ diff --git a/src/tests/MaxFuncBodyLengthRuleTests.ts b/src/tests/MaxFuncBodyLengthRuleTests.ts index 41d2ab0d2..92c768936 100644 --- a/src/tests/MaxFuncBodyLengthRuleTests.ts +++ b/src/tests/MaxFuncBodyLengthRuleTests.ts @@ -4,7 +4,7 @@ import { TestHelper } from './TestHelper'; * Unit tests. */ describe('maxFuncBodyLengthRule', (): void => { - let options; + let options: any; const script: string = ` // https://github.com/Microsoft/tslint-microsoft-contrib/issues/468#issuecomment-407456317 diff --git a/src/tests/NoOctalLiteralTests.ts b/src/tests/NoOctalLiteralTests.ts index 97de348db..ebbf75bfd 100644 --- a/src/tests/NoOctalLiteralTests.ts +++ b/src/tests/NoOctalLiteralTests.ts @@ -101,6 +101,13 @@ function demoScriptFail1() { "startPosition": { "character": 25, "line": 9 } }, { + "failure": "Octal literals should not be used: \\025", + "name": "file.ts", + "ruleName": "no-octal-literal", + "ruleSeverity": "ERROR", + "startPosition": { "character": 25, "line": 10 } + }, + { "failure": "Octal literals should not be used: \\0", "name": "file.ts", "ruleName": "no-octal-literal", diff --git a/src/tests/TestHelper.ts b/src/tests/TestHelper.ts index 3df3f61ae..27f691df0 100644 --- a/src/tests/TestHelper.ts +++ b/src/tests/TestHelper.ts @@ -48,42 +48,51 @@ export module TestHelper { fix?: Fix; } - export function assertNoViolation(ruleName: string, - inputFileOrScript: string, - useTypeChecker: boolean = false) { + export function assertNoViolation( + ruleName: string, + inputFileOrScript: string, + useTypeChecker: boolean = false + ) { runRuleAndEnforceAssertions(ruleName, null, inputFileOrScript, [], useTypeChecker); } - export function assertNoViolationWithOptions(ruleName: string, - options: any[], - inputFileOrScript: string, - useTypeChecker: boolean = false) { + export function assertNoViolationWithOptions( + ruleName: string, + options: any[], + inputFileOrScript: string, + useTypeChecker: boolean = false + ) { runRuleAndEnforceAssertions(ruleName, options, inputFileOrScript, [], useTypeChecker); } - export function assertViolationsWithOptions(ruleName: string, - options: any[], - inputFileOrScript: string, - expectedFailures: ExpectedFailure[], - useTypeChecker: boolean = false) { + export function assertViolationsWithOptions( + ruleName: string, + options: any[], + inputFileOrScript: string, + expectedFailures: ExpectedFailure[], + useTypeChecker: boolean = false + ) { runRuleAndEnforceAssertions(ruleName, options, inputFileOrScript, expectedFailures, useTypeChecker); } - export function assertViolations(ruleName: string, - inputFileOrScript: string, - expectedFailures: ExpectedFailure[], - useTypeChecker: boolean = false) { + export function assertViolations( + ruleName: string, + inputFileOrScript: string, + expectedFailures: ExpectedFailure[], + useTypeChecker: boolean = false + ) { runRuleAndEnforceAssertions(ruleName, null, inputFileOrScript, expectedFailures, useTypeChecker); } - export function assertViolationsWithTypeChecker(ruleName: string, - inputFileOrScript: string, - expectedFailures: ExpectedFailure[]) { + export function assertViolationsWithTypeChecker( + ruleName: string, + inputFileOrScript: string, + expectedFailures: ExpectedFailure[]) { runRuleAndEnforceAssertions(ruleName, null, inputFileOrScript, expectedFailures, true); } export function runRule( - ruleName : string, - userOptions: string[], + ruleName: string, + userOptions: string[] | null, inputFileOrScript : string, - useTypeChecker : boolean = false): Lint.LintResult { - + useTypeChecker : boolean = false + ): Lint.LintResult { const configuration: Lint.Configuration.IConfigurationFile = { extends: [], jsRules: new Map>(), @@ -139,9 +148,13 @@ export module TestHelper { return result; } - function runRuleAndEnforceAssertions(ruleName : string, userOptions: string[], inputFileOrScript : string, - expectedFailures : ExpectedFailure[], useTypeChecker: boolean = false) { - + function runRuleAndEnforceAssertions( + ruleName: string, + userOptions: string[] | null, + inputFileOrScript: string, + expectedFailures: ExpectedFailure[], + useTypeChecker: boolean = false + ) { const lintResult: Lint.LintResult = runRule(ruleName, userOptions, inputFileOrScript, useTypeChecker); const actualFailures: ExpectedFailure[] = JSON.parse(lintResult.output); @@ -165,6 +178,7 @@ export module TestHelper { }); const errorMessage = `Wrong # of failures: \n${JSON.stringify(actualFailures, null, 2)}`; + chai.assert.equal(actualFailures.length, expectedFailures.length, errorMessage); expectedFailures.forEach((expected: ExpectedFailure, index: number): void => { diff --git a/src/underscoreConsistentInvocationRule.ts b/src/underscoreConsistentInvocationRule.ts index cac8a81dc..985833df6 100644 --- a/src/underscoreConsistentInvocationRule.ts +++ b/src/underscoreConsistentInvocationRule.ts @@ -83,8 +83,8 @@ class UnderscoreConsistentInvocationRuleWalker extends ErrorTolerantWalker { const propExpression: ts.PropertyAccessExpression = node.expression; if (propExpression.expression.kind === ts.SyntaxKind.CallExpression) { const call: ts.CallExpression = propExpression.expression; - const target: string = AstUtils.getFunctionTarget(call); - const functionName: string = AstUtils.getFunctionName(call); + const target = AstUtils.getFunctionTarget(call); + const functionName = AstUtils.getFunctionName(call); if (target == null && functionName === '_' && call.arguments.length === 1) { const underscoreFunctionName = AstUtils.getFunctionName(node); return FUNCTION_NAMES.indexOf(underscoreFunctionName) > -1; @@ -95,7 +95,7 @@ class UnderscoreConsistentInvocationRuleWalker extends ErrorTolerantWalker { } private isStaticUnderscoreInvocation(node: ts.CallExpression) { - const target: string = AstUtils.getFunctionTarget(node); + const target = AstUtils.getFunctionTarget(node); if (target !== '_') { return false; } diff --git a/src/utils/AstUtils.ts b/src/utils/AstUtils.ts index 04a124294..a1054839f 100644 --- a/src/utils/AstUtils.ts +++ b/src/utils/AstUtils.ts @@ -13,18 +13,18 @@ export module AstUtils { } } - export function getFunctionName(node : ts.CallExpression | ts.NewExpression) : string { + export function getFunctionName(node: ts.CallExpression | ts.NewExpression): string { const expression: ts.Expression = node.expression; - let functionName : string = (expression).text; + let functionName: string = (expression).text; if (functionName === undefined && (expression).name) { functionName = (expression).name.text; } return functionName; } - export function getFunctionTarget(expression: ts.CallExpression) : string { + export function getFunctionTarget(expression: ts.CallExpression): string | null { if (expression.expression.kind === ts.SyntaxKind.PropertyAccessExpression) { - const propExp : ts.PropertyAccessExpression = expression.expression; + const propExp: ts.PropertyAccessExpression = expression.expression; return propExp.expression.getText(); } return null; @@ -34,12 +34,12 @@ export module AstUtils { return functionTarget === '$' || /^(jquery)$/i.test(functionTarget); } - export function hasModifier(modifiers : ts.ModifiersArray, modifierKind : number) : boolean { + export function hasModifier(modifiers: ts.ModifiersArray, modifierKind: number): boolean { if (modifiers == null) { return false; } - let result : boolean = false; - modifiers.forEach((modifier : ts.Node) : void => { + let result: boolean = false; + modifiers.forEach((modifier: ts.Node): void => { if (modifier.kind === modifierKind) { result = true; } @@ -47,44 +47,49 @@ export module AstUtils { return result; } - export function dumpTypeInfo(expression : ts.Expression, languageServices: ts.LanguageService, typeChecker : ts.TypeChecker) : void { + export function dumpTypeInfo(expression: ts.Expression, languageServices: ts.LanguageService, typeChecker: ts.TypeChecker): void { /* tslint:disable:no-console */ console.log(expression.getFullText()); console.log('\tkind: ' + expression.kind); if (expression.kind === ts.SyntaxKind.Identifier || expression.kind === ts.SyntaxKind.PropertyAccessExpression) { - const definitionInfo : ts.DefinitionInfo[] = languageServices.getDefinitionAtPosition('file.ts', expression.getStart()); - if (definitionInfo) { - definitionInfo.forEach((info : ts.DefinitionInfo, index : number) : void => { + const definitionInfo = languageServices.getDefinitionAtPosition('file.ts', expression.getStart()); + if (definitionInfo !== undefined) { + definitionInfo.forEach((info: ts.DefinitionInfo, index: number): void => { console.log('\tdefinitionInfo-' + index); console.log('\t\tkind: ' + info.kind); console.log('\t\tname: ' + info.name); }); } - const typeInfo : ts.DefinitionInfo[] = languageServices.getTypeDefinitionAtPosition('file.ts', expression.getStart()); - if (typeInfo) { - typeInfo.forEach((info : ts.DefinitionInfo, index : number) : void => { + const typeInfo = languageServices.getTypeDefinitionAtPosition('file.ts', expression.getStart()); + if (typeInfo !== undefined) { + typeInfo.forEach((info: ts.DefinitionInfo, index: number): void => { console.log('\ttypeDefinitionInfo-' + index); console.log('\t\tkind: ' + info.kind); console.log('\t\tname: ' + info.name); }); } - const quickInfo : ts.QuickInfo = languageServices.getQuickInfoAtPosition('file.ts', expression.getStart()); - console.log('\tquickInfo.kind = ' + quickInfo.kind); - console.log('\tquickInfo.kindModifiers= ' + quickInfo.kindModifiers); - console.log('\tquickInfo.textSpan = ' + quickInfo.textSpan.start); - console.log('\tquickInfo.displayParts = ' + quickInfo.displayParts[0].text); - console.log('\tquickInfo.displayParts = ' + quickInfo.displayParts[0].kind); + const quickInfo = languageServices.getQuickInfoAtPosition('file.ts', expression.getStart()); + if (quickInfo !== undefined) { + console.log('\tquickInfo.kind = ' + quickInfo.kind); + console.log('\tquickInfo.kindModifiers= ' + quickInfo.kindModifiers); + console.log('\tquickInfo.textSpan = ' + quickInfo.textSpan.start); - const expressionType : ts.Type = typeChecker.getTypeAtLocation(expression); - console.log('\ttypeChecker.typeToString : ' + typeChecker.typeToString(expressionType)); + if (quickInfo.displayParts !== undefined) { + console.log('\tquickInfo.displayParts = ' + quickInfo.displayParts[0].text); + console.log('\tquickInfo.displayParts = ' + quickInfo.displayParts[0].kind); + } + } + + const expressionType: ts.Type = typeChecker.getTypeAtLocation(expression); + console.log('\ttypeChecker.typeToString: ' + typeChecker.typeToString(expressionType)); console.log('\ttype.flags: ' + expressionType.flags); console.log('\ttype.symbol: ' + expressionType.symbol); - const expressionSymbol : ts.Symbol = typeChecker.getSymbolAtLocation(expression); + const expressionSymbol = typeChecker.getSymbolAtLocation(expression); if (expressionSymbol == null) { console.log('\tsymbol: ' + expressionSymbol); } else { @@ -93,7 +98,7 @@ export module AstUtils { console.log('\tsymbol.declarations: ' + expressionSymbol.declarations); } - const contextualType : ts.Type = typeChecker.getContextualType(expression); + const contextualType = typeChecker.getContextualType(expression); if (contextualType == null) { console.log('\tcontextualType: ' + contextualType); } else { @@ -104,7 +109,7 @@ export module AstUtils { /* tslint:enable:no-console */ } - export function isPrivate(node: ts.Node) : boolean { + export function isPrivate(node: ts.Node): boolean { /* tslint:disable:no-bitwise */ if ((ts).NodeFlags.Private != null) { return !!(node.flags & (ts).NodeFlags.Private); @@ -114,7 +119,7 @@ export module AstUtils { /* tslint:enable:no-bitwise */ } - export function isProtected(node: ts.Node) : boolean { + export function isProtected(node: ts.Node): boolean { /* tslint:disable:no-bitwise */ if ((ts).NodeFlags.Protected != null) { return !!(node.flags & (ts).NodeFlags.Protected); @@ -124,7 +129,7 @@ export module AstUtils { /* tslint:enable:no-bitwise */ } - export function isPublic(node: ts.Node) : boolean { + export function isPublic(node: ts.Node): boolean { /* tslint:disable:no-bitwise */ if ((ts).NodeFlags.Public != null) { return !!(node.flags & (ts).NodeFlags.Public); @@ -134,7 +139,7 @@ export module AstUtils { /* tslint:enable:no-bitwise */ } - export function isStatic(node: ts.Node) : boolean { + export function isStatic(node: ts.Node): boolean { /* tslint:disable:no-bitwise */ if ((ts).NodeFlags.Static != null) { return !!(node.flags & (ts).NodeFlags.Static); @@ -197,7 +202,8 @@ export module AstUtils { if (node.kind === ts.SyntaxKind.VariableDeclaration && node.parent.kind === ts.SyntaxKind.VariableDeclarationList && node.parent.parent.kind === ts.SyntaxKind.VariableStatement) { - if (AstUtils.hasModifier(node.parent.parent.modifiers, ts.SyntaxKind.ExportKeyword)) { + if (node.parent.parent.modifiers !== undefined + && AstUtils.hasModifier(node.parent.parent.modifiers, ts.SyntaxKind.ExportKeyword)) { return true; } } @@ -215,8 +221,8 @@ export module AstUtils { (node.kind === ts.SyntaxKind.ObjectLiteralExpression || node.kind === ts.SyntaxKind.ArrayLiteralExpression); } - export function findParentBlock(child: ts.Node) : ts.Node { - let parent : ts.Node = child.parent; + export function findParentBlock(child: ts.Node): ts.Node { + let parent: ts.Node = child.parent; while (parent != null) { if (parent.kind === ts.SyntaxKind.Block) { return parent; @@ -226,7 +232,7 @@ export module AstUtils { throw new Error('Could not determine parent block of node: ' + child); } - export function isSameIdentifer(source : ts.Node, target: ts.Node) : boolean { + export function isSameIdentifer(source: ts.Node, target: ts.Node): boolean { if (source == null || target == null) { return false; } @@ -262,7 +268,7 @@ export module AstUtils { return false; } - export function isUndefined(node: ts.Expression): boolean { + export function isUndefined(node: ts.Expression | null | undefined): boolean { if (node != null) { if (node.kind === ts.SyntaxKind.Identifier) { return node.getText() === 'undefined'; @@ -271,7 +277,7 @@ export module AstUtils { return false; } - export function isConstant(node: ts.Expression): boolean { + export function isConstant(node: ts.Expression | null | undefined): boolean { if (node == null) { return false; } diff --git a/src/utils/ChaiUtils.ts b/src/utils/ChaiUtils.ts index de543ac95..0af70e7b0 100644 --- a/src/utils/ChaiUtils.ts +++ b/src/utils/ChaiUtils.ts @@ -6,7 +6,7 @@ import * as ts from 'typescript'; export module ChaiUtils { export function isExpectInvocation(node: ts.PropertyAccessExpression | ts.CallExpression): boolean { - const callExpression: ts.CallExpression = getLeftMostCallExpression(node); + const callExpression = getLeftMostCallExpression(node); if (callExpression == null) { return false; } @@ -14,8 +14,8 @@ export module ChaiUtils { return /.*\.?expect/.test(callExpression.expression.getText()); } - export function getExpectInvocation(node: ts.PropertyAccessExpression | ts.CallExpression): ts.CallExpression { - const callExpression: ts.CallExpression = getLeftMostCallExpression(node, false); + export function getExpectInvocation(node: ts.PropertyAccessExpression | ts.CallExpression): ts.CallExpression | null { + const callExpression = getLeftMostCallExpression(node, false); if (callExpression == null) { return null; } @@ -29,7 +29,8 @@ export module ChaiUtils { export function getLeftMostCallExpression( node: ts.PropertyAccessExpression | ts.CallExpression, - checkParent: boolean = false): ts.CallExpression { + checkParent: boolean = false + ): ts.CallExpression | null { let leftSide: ts.Node = node.expression; while (leftSide != null) { if (leftSide.kind === ts.SyntaxKind.CallExpression) { @@ -45,15 +46,15 @@ export module ChaiUtils { return null; } - export function getFirstExpectCallParameter(node: ts.CallExpression): ts.Node { - const expectCall: ts.CallExpression = ChaiUtils.getLeftMostCallExpression(node); - if (expectCall.arguments.length > 0) { + export function getFirstExpectCallParameter(node: ts.CallExpression): ts.Node | null { + const expectCall = ChaiUtils.getLeftMostCallExpression(node); + if (expectCall != null && expectCall.arguments.length > 0) { return expectCall.arguments[0]; } return null; } - export function getFirstExpectationParameter(node: ts.CallExpression): ts.Node { + export function getFirstExpectationParameter(node: ts.CallExpression): ts.Node | null { if (node.arguments.length > 0) { return node.arguments[0]; } diff --git a/src/utils/ErrorTolerantWalker.ts b/src/utils/ErrorTolerantWalker.ts index ad218f161..7fbab788e 100644 --- a/src/utils/ErrorTolerantWalker.ts +++ b/src/utils/ErrorTolerantWalker.ts @@ -31,7 +31,7 @@ export class ErrorTolerantWalker extends Lint.RuleWalker { private getClassName(): string { // Some versions of IE have the word "function" in the constructor name and // have the function body there as well. This rips out and returns the function name. - const result: string = this.constructor.toString().match(/function\s+([\w\$]+)\s*\(/)[1] || ''; + const result: string = this.constructor.toString().match(/function\s+([\w\$]+)\s*\(/)![1] || ''; if (result == null || result.length === 0) { throw new Error('Could not determine class name from input: ' + this.constructor.toString()); } diff --git a/src/utils/JsxAttribute.ts b/src/utils/JsxAttribute.ts index a997e37e7..74b53d34b 100644 --- a/src/utils/JsxAttribute.ts +++ b/src/utils/JsxAttribute.ts @@ -15,7 +15,7 @@ import { isTrueKeyword } from './TypeGuard'; -export function getPropName(node: ts.JsxAttribute): string { +export function getPropName(node: ts.JsxAttribute): string | undefined { if (!isJsxAttribute(node)) { throw new Error('The node must be a JsxAttribute collected by the AST parser.'); } @@ -32,18 +32,19 @@ export function getPropName(node: ts.JsxAttribute): string { * @example *
*/ -export function getStringLiteral(node: ts.JsxAttribute | ts.JsxSpreadAttribute): string { +export function getStringLiteral(node: ts.JsxAttribute | ts.JsxSpreadAttribute): string | undefined { if (!isJsxAttribute(node)) { throw new Error('The node must be a JsxAttribute collected by the AST parser.'); } - const initializer: ts.Expression = node == null ? null : node.initializer; + const initializer = node == null ? null : node.initializer; if (!initializer) { // return ''; } else if (isStringLiteral(initializer)) { // return initializer.text.trim(); - } else if (isJsxExpression(initializer) && isStringLiteral(initializer.expression)) { // + } else if (isJsxExpression(initializer) && initializer.expression != null + && isStringLiteral(initializer.expression)) { // return (initializer.expression).text; } else if (isJsxExpression(initializer) && !initializer.expression) { // return ''; @@ -61,13 +62,17 @@ export function getStringLiteral(node: ts.JsxAttribute | ts.JsxSpreadAttribute): * @example *
*/ -export function getBooleanLiteral(node: ts.JsxAttribute): boolean { +export function getBooleanLiteral(node: ts.JsxAttribute): boolean | undefined { if (!isJsxAttribute(node)) { throw new Error('The node must be a JsxAttribute collected by the AST parser.'); } - const initializer: ts.Expression = node == null ? null : node.initializer; - const getBooleanFromString: (value: string) => boolean = (value: string) => { + const initializer = node == null ? null : node.initializer; + if (initializer == null) { + return false; + } + + const getBooleanFromString: (value: string) => boolean | undefined = (value: string) => { if (value.toLowerCase() === 'true') { return true; } else if (value.toLowerCase() === 'false') { @@ -80,9 +85,11 @@ export function getBooleanLiteral(node: ts.JsxAttribute): boolean { if (isStringLiteral(initializer)) { return getBooleanFromString(initializer.text); } else if (isJsxExpression(initializer)) { - const expression: ts.Expression = initializer.expression; + const expression = initializer.expression; - if (isStringLiteral(expression)) { + if (expression == null) { + return undefined; + } else if (isStringLiteral(expression)) { return getBooleanFromString(expression.text); } else { if (isTrueKeyword(expression)) { @@ -99,16 +106,12 @@ export function getBooleanLiteral(node: ts.JsxAttribute): boolean { } export function isEmpty(node: ts.JsxAttribute): boolean { - const initializer: ts.Expression = node == null ? null : node.initializer; + const initializer = node == null ? null : node.initializer; if (initializer == null) { return true; } else if (isStringLiteral(initializer)) { return initializer.text.trim() === ''; - } else if (initializer.kind === ts.SyntaxKind.Identifier) { - return initializer.getText() === 'undefined'; - } else if (initializer.kind === ts.SyntaxKind.NullKeyword) { - return true; } else if ((initializer).expression != null) { const expression: ts.Expression = (initializer).expression; if (expression.kind === ts.SyntaxKind.Identifier) { @@ -125,14 +128,14 @@ export function isEmpty(node: ts.JsxAttribute): boolean { * @example *
*/ -export function getNumericLiteral(node: ts.JsxAttribute): string { +export function getNumericLiteral(node: ts.JsxAttribute): string | undefined { if (!isJsxAttribute(node)) { throw new Error('The node must be a JsxAttribute collected by the AST parser.'); } - const initializer: ts.Expression = node == null ? null : node.initializer; + const initializer = node == null ? null : node.initializer; - return isJsxExpression(initializer) && isNumericLiteral(initializer.expression) + return initializer != null && isJsxExpression(initializer) && initializer.expression != null && isNumericLiteral(initializer.expression) ? (initializer.expression).text : undefined; } @@ -141,8 +144,8 @@ export function getNumericLiteral(node: ts.JsxAttribute): string { * Get an array of attributes in the given node. * It contains JsxAttribute and JsxSpreadAttribute. */ -export function getAllAttributesFromJsxElement(node: ts.Node): ts.NodeArray { - let attributes: ts.NodeArray = null; +export function getAllAttributesFromJsxElement(node: ts.Node): ts.NodeArray | null { + let attributes: ts.NodeArray | null = null; if (node == null) { return attributes; @@ -165,12 +168,20 @@ export function getAllAttributesFromJsxElement(node: ts.Node): ts.NodeArray { - if (isJsxAttribute(attr)) { - attributesDictionary[getPropName(attr).toLowerCase()] = attr; - } - }); + if (attributes != null) { + attributes.forEach((attr) => { + if (!isJsxAttribute(attr)) { + return; + } + + const propName = getPropName(attr); + if (propName != null) { + attributesDictionary[propName.toLowerCase()] = attr; + } + }); + } return attributesDictionary; } @@ -181,13 +192,13 @@ export function getJsxAttributesFromJsxElement(node: ts.Node): { [propName: stri * @param exceptTagName - the element's tagName you want to get. * @return a element. */ -export function getJsxElementFromCode(code: string, exceptTagName: string): ts.JsxElement | ts.JsxSelfClosingElement { +export function getJsxElementFromCode(code: string, exceptTagName: string): ts.JsxElement | ts.JsxSelfClosingElement | undefined { const sourceFile: ts.SourceFile = ts.createSourceFile('test.tsx', code, ts.ScriptTarget.ES2015, true); return delintNode(sourceFile, exceptTagName); } -function delintNode(node: ts.Node, tagName: string): ts.JsxElement | ts.JsxSelfClosingElement { +function delintNode(node: ts.Node, tagName: string): ts.JsxElement | ts.JsxSelfClosingElement | undefined { if (isJsxElement(node) && node.openingElement.tagName.getText() === tagName) { return node; } else if (isJsxSelfClosingElement(node) && node.tagName.getText() === tagName) { @@ -203,7 +214,7 @@ function delintNode(node: ts.Node, tagName: string): ts.JsxElement | ts.JsxSelfC * Get ancestor node whose tagName is ancestorTagName for a node. * @return the ancestor node or undefined if the ancestor node is not exist. */ -export function getAncestorNode(node: ts.Node, ancestorTagName: string): ts.JsxElement { +export function getAncestorNode(node: ts.Node, ancestorTagName: string): ts.JsxElement | undefined { if (!node) { return undefined; } diff --git a/src/utils/NoStringParameterToFunctionCallWalker.ts b/src/utils/NoStringParameterToFunctionCallWalker.ts index 8d4fc770c..8a77158c4 100644 --- a/src/utils/NoStringParameterToFunctionCallWalker.ts +++ b/src/utils/NoStringParameterToFunctionCallWalker.ts @@ -26,9 +26,9 @@ export class NoStringParameterToFunctionCallWalker extends ScopedSymbolTrackingW } private validateExpression(node : ts.CallExpression) : void { - const functionName : string = AstUtils.getFunctionName(node); - const functionTarget : string = AstUtils.getFunctionTarget(node); - const functionTargetType : string = this.getFunctionTargetType(node); + const functionName = AstUtils.getFunctionName(node); + const functionTarget = AstUtils.getFunctionTarget(node); + const functionTargetType = this.getFunctionTargetType(node); const firstArg : ts.Expression = node.arguments[0]; if (functionName === this.targetFunctionName && firstArg != null) { if (functionTarget) { diff --git a/src/utils/Scope.ts b/src/utils/Scope.ts index de2bfe39b..625e03107 100644 --- a/src/utils/Scope.ts +++ b/src/utils/Scope.ts @@ -7,10 +7,10 @@ import {AstUtils} from './AstUtils'; * Tracks nested scope of variables. */ export class Scope { - public parent: Scope; + public parent: Scope | null; private symbols: { [index: string]: number } = {}; - constructor(parent: Scope) { + constructor(parent: Scope | null) { this.parent = parent; } diff --git a/src/utils/ScopedSymbolTrackingWalker.ts b/src/utils/ScopedSymbolTrackingWalker.ts index 4228dbd59..6e70ad6bb 100644 --- a/src/utils/ScopedSymbolTrackingWalker.ts +++ b/src/utils/ScopedSymbolTrackingWalker.ts @@ -11,7 +11,7 @@ import {Scope} from './Scope'; */ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { private typeChecker?: ts.TypeChecker; - private scope: Scope; + private scope: Scope | null = null; constructor(sourceFile : ts.SourceFile, options : Lint.IOptions, program? : ts.Program) { super(sourceFile, options); @@ -21,7 +21,7 @@ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { } } - protected getFunctionTargetType(expression: ts.CallExpression) : string { + protected getFunctionTargetType(expression: ts.CallExpression): string | null { if (expression.expression.kind === ts.SyntaxKind.PropertyAccessExpression && this.typeChecker) { const propExp : ts.PropertyAccessExpression = expression.expression; const targetType: ts.Type = this.typeChecker.getTypeAtLocation(propExp.expression); @@ -45,7 +45,7 @@ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { } // is the symbol something we are tracking in scope ourselves? - if (this.scope.isFunctionSymbol(expression.getText())) { + if (this.scope != null && this.scope.isFunctionSymbol(expression.getText())) { return true; } @@ -69,9 +69,12 @@ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { return true; } - const signature : ts.Signature = this.typeChecker.getResolvedSignature(expression); - const expressionType : ts.Type = this.typeChecker.getReturnTypeOfSignature(signature); - return this.isFunctionType(expressionType, this.typeChecker); + const signature = this.typeChecker.getResolvedSignature(expression); + + if (signature !== undefined) { + const expressionType = this.typeChecker.getReturnTypeOfSignature(signature); + return this.isFunctionType(expressionType, this.typeChecker); + } } catch (e) { // this exception is only thrown in unit tests, not the node debugger :( return false; @@ -86,10 +89,10 @@ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { } private isFunctionType(expressionType : ts.Type, typeChecker : ts.TypeChecker) : boolean { - const signatures : ts.Signature[] = typeChecker.getSignaturesOfType(expressionType, ts.SignatureKind.Call); + const signatures = typeChecker.getSignaturesOfType(expressionType, ts.SignatureKind.Call); if (signatures != null && signatures.length > 0) { - const signatureDeclaration : ts.SignatureDeclaration = signatures[0].declaration; - if (signatureDeclaration.kind === ts.SyntaxKind.FunctionType) { + const signatureDeclaration = signatures[0].declaration; + if (signatureDeclaration != null && signatureDeclaration.kind === ts.SyntaxKind.FunctionType) { return true; // variables of type function are allowed to be passed as parameters } } @@ -105,28 +108,28 @@ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { protected visitModuleDeclaration(node: ts.ModuleDeclaration): void { this.scope = new Scope(this.scope); - this.scope.addGlobalScope(node.body, this.getSourceFile(), this.getOptions()); + this.scope.addGlobalScope(node.body!, this.getSourceFile(), this.getOptions()); super.visitModuleDeclaration(node); this.scope = this.scope.parent; } protected visitClassDeclaration(node: ts.ClassDeclaration): void { - this.scope = new Scope(this.scope); + const scope = this.scope = new Scope(this.scope); node.members.forEach((element: ts.ClassElement): void => { - const prefix: string = AstUtils.isStatic(element) + const prefix: string = AstUtils.isStatic(element) && node.name != null ? node.name.getText() + '.' : 'this.'; if (element.kind === ts.SyntaxKind.MethodDeclaration) { // add all declared methods as valid functions - this.scope.addFunctionSymbol(prefix + (element).name.getText()); + scope.addFunctionSymbol(prefix + (element).name.getText()); } else if (element.kind === ts.SyntaxKind.PropertyDeclaration) { const prop: ts.PropertyDeclaration = element; // add all declared function properties as valid functions if (AstUtils.isDeclarationFunctionType(prop)) { - this.scope.addFunctionSymbol(prefix + (element).name.getText()); + scope.addFunctionSymbol(prefix + (element).name.getText()); } else { - this.scope.addNonFunctionSymbol(prefix + (element).name.getText()); + scope.addNonFunctionSymbol(prefix + (element).name.getText()); } } }); @@ -178,9 +181,9 @@ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { protected visitVariableDeclaration(node: ts.VariableDeclaration): void { if (AstUtils.isDeclarationFunctionType(node)) { - this.scope.addFunctionSymbol(node.name.getText()); + this.scope!.addFunctionSymbol(node.name.getText()); } else { - this.scope.addNonFunctionSymbol(node.name.getText()); + this.scope!.addNonFunctionSymbol(node.name.getText()); } super.visitVariableDeclaration(node); } diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 8fdbc5696..b149e0569 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -6,7 +6,7 @@ export module Utils { /** * Logical 'any' or 'exists' function. */ - export function exists(list : ReadonlyArray, predicate: (t: T) => boolean) : boolean { + export function exists(list : ReadonlyArray | null | undefined, predicate: (t: T) => boolean) : boolean { if (list != null) { for (let i = 0; i < list.length; i++) { const obj : T = list[i]; @@ -21,7 +21,7 @@ export module Utils { /** * A contains function. */ - export function contains(list: ReadonlyArray, element: T): boolean { + export function contains(list: ReadonlyArray | null | undefined, element: T): boolean { return exists(list, (item: T): boolean => { return item === element; }); @@ -30,12 +30,15 @@ export module Utils { /** * A removeAll function. */ - export function removeAll(source: ReadonlyArray, elementsToRemove: ReadonlyArray): T[] { + export function removeAll( + source: ReadonlyArray | null | undefined, + elementsToRemove: ReadonlyArray | null | undefined + ): T[] { if (source == null || source.length === 0) { return []; } if (elementsToRemove == null || elementsToRemove.length === 0) { - return [].concat(source); // be sure to return a copy of the array + return [...source]; // be sure to return a copy of the array } return source.filter((sourceElement: T): boolean => { @@ -50,7 +53,7 @@ export module Utils { return removeAll(source, [elementToRemove]); } - export function trimTo(source: string, maxLength: number): string { + export function trimTo(source: string | null | undefined, maxLength: number): string { if (source == null) { return ''; } diff --git a/src/utils/getImplicitRole.ts b/src/utils/getImplicitRole.ts index eb9e34dfe..c77a32335 100644 --- a/src/utils/getImplicitRole.ts +++ b/src/utils/getImplicitRole.ts @@ -1,5 +1,5 @@ import * as ts from 'typescript'; -import * as implicitRoles from './implicitRoles'; +import { implicitRoles } from './implicitRoles'; import { isJsxElement, isJsxSelfClosingElement, isJsxOpeningElement } from './TypeGuard'; /** @@ -10,10 +10,12 @@ import { isJsxElement, isJsxSelfClosingElement, isJsxOpeningElement } from './Ty * A reference about implicit role: https://www.w3.org/TR/html-aria/#sec-strong-native-semantics. * A reference about no corresponding role: https://www.w3.org/TR/html-aria/#dfn-no-corresponding-role. */ -export function getImplicitRole(node: ts.Node): string { - let tagName: string; +export function getImplicitRole(node: ts.Node | null | undefined): string | undefined { + let tagName: string | undefined; - if (isJsxElement(node)) { + if (node == null) { + return undefined; + } else if (isJsxElement(node)) { tagName = node.openingElement.tagName.getText(); } else if (isJsxSelfClosingElement(node)) { tagName = node.tagName.getText(); @@ -23,5 +25,9 @@ export function getImplicitRole(node: ts.Node): string { tagName = undefined; } - return tagName && implicitRoles[tagName] && implicitRoles[tagName](node); + if (tagName === undefined || !(tagName in implicitRoles)) { + return undefined; + } + + return implicitRoles[tagName](node); } diff --git a/src/utils/implicitRoles/a.ts b/src/utils/implicitRoles/a.ts index c9e4abca3..470b0a69b 100644 --- a/src/utils/implicitRoles/a.ts +++ b/src/utils/implicitRoles/a.ts @@ -6,7 +6,7 @@ const hrefString: string = 'href'; /** * @Returns the implicit role for an anchor tag. */ -function getImplicitRoleForAnchor(node: ts.Node): string { +function getImplicitRoleForAnchor(node: ts.Node): string | undefined { return getJsxAttributesFromJsxElement(node)[hrefString] ? 'link' : undefined; } diff --git a/src/utils/implicitRoles/area.ts b/src/utils/implicitRoles/area.ts index f3ebd7690..8ffa6fb0d 100644 --- a/src/utils/implicitRoles/area.ts +++ b/src/utils/implicitRoles/area.ts @@ -6,7 +6,7 @@ const hrefString: string = 'href'; /** * @Returns the implicit role for an area tag. */ -function getImplicitRoleForArea(node: ts.Node): string { +function getImplicitRoleForArea(node: ts.Node): string | undefined { return getJsxAttributesFromJsxElement(node)[hrefString] ? 'link' : undefined; } diff --git a/src/utils/implicitRoles/footer.ts b/src/utils/implicitRoles/footer.ts index ecee0a7ec..45485a547 100644 --- a/src/utils/implicitRoles/footer.ts +++ b/src/utils/implicitRoles/footer.ts @@ -4,7 +4,7 @@ import { getAncestorNode } from '../JsxAttribute'; /** * @Returns the implicit role for a footer tag. */ -function getImplicitRoleForFooter(node: ts.Node): string { +function getImplicitRoleForFooter(node: ts.Node): string | undefined { return getAncestorNode(node, 'article') || getAncestorNode(node, 'section') ? undefined : 'contentinfo'; } diff --git a/src/utils/implicitRoles/header.ts b/src/utils/implicitRoles/header.ts index 515d7aa9f..255a59cf2 100644 --- a/src/utils/implicitRoles/header.ts +++ b/src/utils/implicitRoles/header.ts @@ -4,7 +4,7 @@ import { getAncestorNode } from '../JsxAttribute'; /** * @Returns the implicit role for a header tag. */ -function getImplicitRoleForHeader(node: ts.Node): string { +function getImplicitRoleForHeader(node: ts.Node): string | undefined { return getAncestorNode(node, 'article') || getAncestorNode(node, 'section') ? undefined : 'banner'; } diff --git a/src/utils/implicitRoles/index.ts b/src/utils/implicitRoles/index.ts index 3c05d1e77..722da1e9b 100644 --- a/src/utils/implicitRoles/index.ts +++ b/src/utils/implicitRoles/index.ts @@ -1,3 +1,5 @@ +import * as ts from 'typescript'; + import { a } from './a'; import { area } from './area'; import { article } from './article'; @@ -48,10 +50,14 @@ import { thead } from './thead'; import { tr } from './tr'; import { ul } from './ul'; +export interface IImplicitRoles { + [i: string]: (node: ts.Node) => string | undefined; +} + /** * Export function for getting implicit role based on tag name. */ -export { +export const implicitRoles: IImplicitRoles = { a, area, article, diff --git a/src/utils/implicitRoles/input.ts b/src/utils/implicitRoles/input.ts index d85ea8e9a..a58d0c567 100644 --- a/src/utils/implicitRoles/input.ts +++ b/src/utils/implicitRoles/input.ts @@ -5,9 +5,9 @@ const typeString: string = 'type'; const listString: string = 'list'; /** - * @Returns the implicit role for an input tag. + * @returns the implicit role for an input tag. */ -function getImplicitRoleForInput(node: ts.Node): string { +function getImplicitRoleForInput(node: ts.Node): string | undefined { const attributes: { [propName: string]: ts.JsxAttribute } = getJsxAttributesFromJsxElement(node); const typeAttribute: ts.JsxAttribute = attributes[typeString]; diff --git a/src/utils/implicitRoles/li.ts b/src/utils/implicitRoles/li.ts index 26c5464cb..395dcb74d 100644 --- a/src/utils/implicitRoles/li.ts +++ b/src/utils/implicitRoles/li.ts @@ -4,13 +4,14 @@ import { isJsxElement } from '../TypeGuard'; /** * @Returns the implicit role for an li tag. */ -function getImplicitRoleForLi(node: ts.Node): string { +function getImplicitRoleForLi(node: ts.Node): string | undefined { const parentNode: ts.Node = node.parent; - let parentTagName: string; + let parentTagName: string | undefined; if (isJsxElement(parentNode)) { parentTagName = parentNode.openingElement.tagName.getText(); } + return (parentTagName === 'ol' || parentTagName === 'ul') ? 'listitem' : undefined; } diff --git a/src/utils/implicitRoles/link.ts b/src/utils/implicitRoles/link.ts index 6b3c8bfcd..7041dd5dd 100644 --- a/src/utils/implicitRoles/link.ts +++ b/src/utils/implicitRoles/link.ts @@ -6,7 +6,7 @@ const hrefString: string = 'href'; /** * @Returns the implicit role for a link tag. */ -function getImplicitRoleForLink(node: ts.Node): string { +function getImplicitRoleForLink(node: ts.Node): string | undefined { return getJsxAttributesFromJsxElement(node)[hrefString] ? 'link' : undefined; } diff --git a/src/utils/implicitRoles/menu.ts b/src/utils/implicitRoles/menu.ts index 990014357..f37d16697 100644 --- a/src/utils/implicitRoles/menu.ts +++ b/src/utils/implicitRoles/menu.ts @@ -6,11 +6,11 @@ const typeString: string = 'type'; /** * @Returns the implicit role for a menu tag. */ -function getImplicitRoleForMenu(node: ts.Node): string { +function getImplicitRoleForMenu(node: ts.Node): string | undefined { const typeAttribute: ts.JsxAttribute = getJsxAttributesFromJsxElement(node)[typeString]; if (typeAttribute) { - const value: string = getStringLiteral(typeAttribute) || undefined; + const value = getStringLiteral(typeAttribute) || undefined; return (value && value.toUpperCase() === 'TOOLBAR') ? 'toolbar' : undefined; } diff --git a/src/utils/implicitRoles/menuitem.ts b/src/utils/implicitRoles/menuitem.ts index e45d3c90f..61e4f8b13 100644 --- a/src/utils/implicitRoles/menuitem.ts +++ b/src/utils/implicitRoles/menuitem.ts @@ -6,11 +6,11 @@ const typeString: string = 'type'; /** * @Returns the implicit role for a menuitem tag. */ -function getImplicitRoleForMenuitem(node: ts.Node): string { +function getImplicitRoleForMenuitem(node: ts.Node): string | undefined { const typeAttribute: ts.JsxAttribute = getJsxAttributesFromJsxElement(node)[typeString]; if (typeAttribute) { - const value: string = getStringLiteral(typeAttribute) || ''; + const value = getStringLiteral(typeAttribute) || ''; switch (value.toUpperCase()) { case 'COMMAND': diff --git a/tsconfig.json b/tsconfig.json index 571bc660d..dcbac87e4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,9 +6,11 @@ "noImplicitReturns": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noImplicitAny": false, - "noImplicitThis": false, - "strictNullChecks": false, + "noImplicitAny": true, + "noImplicitThis": true, + "strictFunctionTypes": true, + "strictPropertyInitialization": true, + "strictNullChecks": true, "declaration": true, "jsx": "react", "lib": [ diff --git a/tslint-warnings.csv b/tslint-warnings.csv index 2d68a5208..df72cd294 100644 --- a/tslint-warnings.csv +++ b/tslint-warnings.csv @@ -4,7 +4,7 @@ align,Enforces vertical alignment.,TSLINTT6VKI6,tslint,Non-SDL,Warning,Low,Oppor CWE 710 - Coding Standards Violation" array-type,Requires using either 'T[]' or 'Array' for arrays.,TSLINT11L733J,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, await-promise,Warns for an awaited value that is not a Promise.,TSLINT14G9IF5,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, -ban-comma-operator,Bans the comma operator.,TSLINT1GR8O3P,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" +ban-comma-operator,Disallows the comma operator to be used.,TSLINT1GR8O3P,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" binary-expression-operand-order,"In a binary expression, a literal should always be on the right-hand side if possible. For example, prefer 'x + 1' over '1 + x'.",TSLINT1RMDJP2,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" callable-types,An interface or literal type with just a call signature can be written as a function type.,TSLINTAJ483,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, chai-prefer-contains-to-index-of,Avoid Chai assertions that invoke indexOf and compare for a -1 result.,TSLINTCSVNDE,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 @@ -15,12 +15,14 @@ class-name,Enforces PascalCased class and interface names.,TSLINT65UF71,tslint,N CWE 710 - Coding Standards Violation" comment-format,Enforces formatting rules for single-line comments.,TSLINT1T6OE84,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" -completed-docs,Enforces documentation for important items be filled out.,TSLINTTVUOIS,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, +completed-docs,Enforces JSDoc comments for important items be filled out.,TSLINTTVUOIS,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, curly,Enforces braces for `if`/`for`/`do`/`while` statements.,TSLINTT90EOE,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"483, 710","CWE 483 - Incorrect Block Delimitation CWE 710 - Coding Standards Violation" cyclomatic-complexity,Enforces a threshold of cyclomatic complexity.,TSLINT1F0UAB2,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, deprecation,Warns when deprecated APIs are used.,TSLINTJ5MPVL,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" encoding,Enforces UTF-8 file encoding.,TSLINT1D4H7JI,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" +file-name-casing,Enforces a consistent file naming convention,TSLINTGCPLQ3,tslint,Non-SDL,Warning,Moderate,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" forin,Requires a `for ... in` statement to be filtered with an `if` statement.,TSLINTTBFHNF,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" function-name,Applies a naming convention to function names and method names,TSLINTN7VHIV,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 @@ -54,6 +56,8 @@ CWE 710 - Coding Standards Violation" mocha-avoid-only,"Do not invoke Mocha's describe.only, it.only or context.only functions.",TSLINT1M1BHOM,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, new-parens,Requires parentheses when invoking a constructor via the `new` keyword.,TSLINTJF199B,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" newline-before-return,Enforces blank line before return when not the only line in the block.,TSLINT1QRE306,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" +newline-per-chained-call,Requires that chained method calls be broken apart onto separate lines.,TSLINTNLMREJ,tslint,Non-SDL,Warning,Moderate,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-any,Disallows usages of `any` as a type declaration.,TSLINTKSGO5V,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" no-arg,Disallows use of `arguments.callee`.,TSLINTKSGO99,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality @@ -98,6 +102,8 @@ no-duplicate-imports,Disallows multiple import statements from the same module., no-duplicate-super,Warns if 'super()' appears twice in a constructor.,TSLINTCBE8BK,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-duplicate-switch-case,Prevents duplicate cases in switch statements.,TSLINT1P1MP3Q,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-duplicate-variable,Disallows duplicate variable declarations in the same block scope.,TSLINT6TMGHL,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,398,"CWE 398 - Indicator of Poor Code Quality" +no-dynamic-delete,Bans usage of the delete operator with computed key expressions.,TSLINT11QVFDQ,tslint,Non-SDL,Warning,Moderate,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-empty,Disallows empty blocks.,TSLINTJ99V50,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,398,"CWE 398 - Indicator of Poor Code Quality" no-empty-interface,Forbids empty interfaces.,TSLINTEPMP7K,tslint,Non-SDL,Warning,Moderate,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" @@ -128,7 +134,7 @@ CWE 710 - Coding Standards Violation" no-invalid-regexp,Do not use invalid regular expression strings in the RegExp constructor.,TSLINT18FB6OK,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, no-invalid-template-strings,Warns on use of `${` in non-template strings.,TSLINT10TRETE,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-invalid-this,Disallows using the `this` keyword outside of classes.,TSLINTD2VI5V,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, -no-irregular-whitespace,Disallow irregular whitespace outside of strings and comments,TSLINT4PJDU4,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" +no-irregular-whitespace,"Disallow irregular whitespace within a file, including strings and comments.",TSLINT4PJDU4,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-jquery-raw-elements,Do not create HTML elements using JQuery and string concatenation. It is error prone and can hide subtle defects.,TSLINTBQ3MR2,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" no-misused-new,Warns on apparent attempts to define constructors for interfaces or `new` for classes.,TSLINTL96MA6,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, @@ -164,7 +170,7 @@ CWE 116 - Improper Encoding or Escaping of Output" no-string-literal,Forbids unnecessary string literal property access. Allows `obj["prop-erty"]` (can't be a regular property access). Disallows `obj["property"]` (should be `obj.property`).,TSLINT2USQI0,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" -no-string-throw,Flags throwing plain strings or concatenations of strings because only Errors produce proper stack traces.,TSLINT1PEDNQ9,tslint,Non-SDL,Warning,High,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, +no-string-throw,Flags throwing plain strings or concatenations of strings.,TSLINT1PEDNQ9,tslint,Non-SDL,Warning,High,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, no-submodule-imports,Disallows importing any submodule.,TSLINT1L169N0,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-suspicious-comment,"Do not use suspicious comments, such as BUG, HACK, FIXME, LATER, LATER2, TODO",TSLINT1MEQM5S,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,546,"CWE 546 - Suspicious Comment" no-switch-case-fall-through,Disallows falling through case statements.,TSLINTOMSBL4,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 484, 710","CWE 398 - Indicator of Poor Code Quality @@ -234,8 +240,12 @@ CWE 710 - Coding Standards Violation" prefer-for-of,Recommends a 'for-of' loop over a standard 'for' loop if the index is only used to access the array being iterated.,TSLINT51MHG7,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, prefer-method-signature,Prefer `foo(): void` over `foo: () => void` in interfaces and types.,TSLINT1LVIQFA,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, prefer-object-spread,Enforces the use of the ES2015 object spread operator over `Object.assign()` where appropriate.,TSLINT10K16KT,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" +prefer-readonly,Requires that private variables are marked as `readonly` if they're never modified outside of the constructor.,TSLINTUCQG50,tslint,Non-SDL,Warning,Moderate,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" prefer-switch,Prefer a `switch` statement to an `if` statement with simple `===` comparisons.,TSLINT682PUI,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" prefer-template,Prefer a template expression over string literal concatenation.,TSLINT1BUICJ8,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" +prefer-while,Prefer `while` loops instead of `for` loops without an initializer and incrementor.,TSLINTG6US9D,tslint,Non-SDL,Warning,Moderate,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" promise-function-async,Requires any function or method that returns a promise to be marked async.,TSLINT1L1TRF8,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, promise-must-complete,"When a Promise instance is created, then either the reject() or resolve() parameter must be called on it within all code branches in the scope.",TSLINT4SIARK,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, quotemark,Requires single or double quotes for string literals.,TSLINTU8MMGA,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 @@ -272,7 +282,7 @@ CWE 710 - Coding Standards Violation" return-undefined,Prefer `return;` in void functions and `return undefined;` in value-returning functions.,TSLINTFU39OI,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" semicolon,Enforces consistent semicolon usage at the end of every statement.,TSLINT1L591RI,tslint,Non-SDL,Warning,Moderate,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" -space-within-parens,Enforces spaces within parentheses or disallow them.,TSLINT1E89MLR,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" +space-within-parens,Enforces spaces within parentheses or disallow them. Empty parentheses () are always allowed.,TSLINT1E89MLR,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" strict-boolean-expressions,"Restricts the types allowed in boolean expressions. By default only booleans are allowed. The following nodes are checked: diff --git a/tslint.json b/tslint.json index ccabd2e8e..42d6d6338 100644 --- a/tslint.json +++ b/tslint.json @@ -210,6 +210,7 @@ "prefer-for-of": false, "prefer-function-over-method": false, "prefer-method-signature": true, + "prefer-readonly": true, "prefer-template": false, "prefer-type-cast": true, "strict-type-predicates": false,