├── .circleci └── config.yml ├── .editorconfig ├── .fimbullinter.yaml ├── .gitignore ├── .npmignore ├── .nycrc.json ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .wotanrc.yaml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── const-parameters.md ├── early-exit.md ├── ext-curly.md ├── naming-convention.md ├── no-accessor-recursion.md ├── no-as-type-assertion.md ├── no-collapsible-if.md ├── no-else-after-return.md ├── no-return-undefined.md ├── no-static-this.md ├── no-unnecessary-else.md ├── no-unnecessary-type-annotation.md ├── no-unused.md ├── no-var-before-return.md ├── object-shorthand-properties-first.md ├── parameter-properties.md ├── prefer-const-enum.md └── prefer-while.md ├── package.json ├── rules ├── constParametersRule.ts ├── earlyExitRule.ts ├── extCurlyRule.ts ├── index.ts ├── namingConventionRule.ts ├── noAccessorRecursionRule.ts ├── noAsTypeAssertionRule.ts ├── noCollapsibleIfRule.ts ├── noElseAfterReturnRule.ts ├── noReturnUndefinedRule.ts ├── noStaticThisRule.ts ├── noUnnecessaryElseRule.ts ├── noUnnecessaryTypeAnnotationRule.ts ├── noUnusedRule.ts ├── noVarBeforeReturnRule.ts ├── objectShorthandPropertiesFirstRule.ts ├── oddnessCheckRule.ts ├── parameterPropertiesRule.ts ├── preferConstEnumRule.ts ├── preferWhileRule.ts └── wotan │ ├── const-parameters.ts │ ├── early-exit.ts │ ├── ext-curly.ts │ ├── naming-convention.ts │ ├── no-accessor-recursion.ts │ ├── no-as-type-assertion.ts │ ├── no-collapsible-if.ts │ ├── no-else-after-return.ts │ ├── no-return-undefined.ts │ ├── no-static-this.ts │ ├── no-unnecessary-else.ts │ ├── no-unnecessary-type-annotation.ts │ ├── no-unused.ts │ ├── no-var-before-return.ts │ ├── object-shorthand-properties-first.ts │ ├── parameter-properties.ts │ ├── prefer-const-enum.ts │ └── prefer-while.ts ├── src ├── rules.ts ├── utils.ts └── walker.ts ├── test └── rules │ ├── const-parameters │ └── default │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── early-exit │ ├── always │ │ ├── test-if-else.ts.lint │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── default │ │ ├── test-if-else.ts.lint │ │ ├── test-switch.ts.lint │ │ ├── test-syntax-kinds.ts.lint │ │ ├── test.ts.lint │ │ └── tslint.json │ └── igore-constructor │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── ext-curly │ ├── braced-child │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── consistent │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── default │ │ ├── test.ts.fix │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── else-braced-child │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── else │ │ ├── test.ts.lint │ │ └── tslint.json │ └── nested-if-else │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── naming-convention │ ├── accessor │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── default │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── filter │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── function-variables │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── multi-format │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── parameter-property │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── strict-formats │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── this │ │ ├── test.ts.lint │ │ └── tslint.json │ └── unused │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── no-accessor-recursion │ └── default │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── no-as-type-assertion │ └── default │ │ ├── ordering.ts.fix │ │ ├── ordering.ts.lint │ │ ├── test.ts.fix │ │ ├── test.ts.lint │ │ ├── test.tsx.lint │ │ └── tslint.json │ ├── no-collapsible-if │ └── default │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── no-else-after-return │ ├── allow-else-if │ │ ├── test.ts.lint │ │ └── tslint.json │ └── default │ │ ├── fix.ts.fix │ │ ├── fix.ts.lint │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── no-return-undefined │ ├── allow-void-expression │ │ ├── test.ts.lint │ │ └── tslint.json │ └── default │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── no-static-this │ └── default │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── no-unnecessary-else │ └── default │ │ ├── test.ts.fix │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── no-unnecessary-type-annotation │ ├── check-return-type │ │ ├── test.ts.lint │ │ ├── tsconfig.json │ │ └── tslint.json │ └── default │ │ ├── test.ts.lint │ │ ├── tsconfig.json │ │ └── tslint.json │ ├── no-unused │ ├── default │ │ ├── declaration-export.d.ts.lint │ │ ├── declaration-global.d.ts.lint │ │ ├── declaration.d.ts.lint │ │ ├── func.ts.lint │ │ ├── global.ts.lint │ │ ├── ignored.ts.lint │ │ ├── imports.js.lint │ │ ├── imports.ts.lint │ │ ├── jsx-fragment.tsx.lint │ │ ├── merge.ts.lint │ │ ├── mixed.lint │ │ ├── parameter.ts.lint │ │ ├── react-self-closing.tsx.lint │ │ ├── react-unused.tsx.lint │ │ ├── react.ts.lint │ │ ├── react.tsx.lint │ │ ├── tslint.json │ │ ├── type-parameter.ts.lint │ │ ├── used-in-declaration.ts.lint │ │ └── write-only.ts.lint │ ├── ignore-imports │ │ ├── declaration.d.ts.lint │ │ ├── imports.js.lint │ │ ├── imports.ts.lint │ │ └── tslint.json │ ├── unused-catch-binding │ │ ├── test.ts.lint │ │ └── tslint.json │ └── unused-named-expression │ │ ├── test.ts.fix │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── no-var-before-return │ ├── allow-destructuring │ │ ├── test.ts.lint │ │ └── tslint.json │ └── default │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── object-shorthand-properties-first │ └── default │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── oddness-check │ └── default │ │ ├── test.ts.fix │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── parameter-properties │ ├── all-or-none │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── leading │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── member-access │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── readonly │ │ ├── test.ts.lint │ │ └── tslint.json │ └── trailing │ │ ├── test.ts.lint │ │ └── tslint.json │ ├── prefer-const-enum │ └── default │ │ ├── global.ts.lint │ │ ├── merged.ts.lint │ │ ├── string.ts.lint │ │ ├── test.ts.fix │ │ ├── test.ts.lint │ │ └── tslint.json │ └── prefer-while │ └── default │ ├── test.ts.fix │ ├── test.ts.lint │ └── tslint.json ├── tsconfig.json ├── tslint.json └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/node:latest 6 | steps: 7 | - checkout 8 | - restore_cache: 9 | key: dependency-cache-{{ checksum "yarn.lock" }} 10 | - run: 11 | name: Install dependencies 12 | command: yarn 13 | - save_cache: 14 | key: dependency-cache-{{ checksum "yarn.lock" }} 15 | paths: 16 | - node_modules 17 | - run: 18 | name: Compile 19 | command: yarn compile 20 | - run: 21 | name: Lint 22 | command: yarn lint 23 | - run: 24 | name: Test with Coverage 25 | command: yarn coverage 26 | - run: 27 | name: Report code coverage 28 | command: yarn report-coverage 29 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_size = 4 8 | indent_style = space 9 | trim_trailing_whitespace = true 10 | 11 | [*.{json,yml,md}] 12 | indent_size = 2 13 | 14 | [*.{fix,lint}] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.fimbullinter.yaml: -------------------------------------------------------------------------------- 1 | project: 2 | - tsconfig.json 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | *.js 3 | /coverage/ 4 | /.nyc_output -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /.nycrc.json 2 | /.vscode 3 | .gitignore 4 | tsconfig.json 5 | tslint.json 6 | *.ts 7 | /test/ 8 | /coverage/ 9 | /.nyc_output 10 | /docs 11 | /.circleci/ 12 | -------------------------------------------------------------------------------- /.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "rules/*.js", 4 | "src/*.js" 5 | ], 6 | "exclude": [ 7 | "rules/index.js" 8 | ], 9 | "extension": [ 10 | ".js" 11 | ], 12 | "reporter": [ 13 | "text-summary", 14 | "html", 15 | "lcov" 16 | ], 17 | "sourceMap": true, 18 | "all": true 19 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/node_modules/.bin/tslint", 9 | "stopOnEntry": false, 10 | "args": ["--test", "test/rules/no-unnecessary-type-annotation/default"], 11 | "cwd": "${workspaceRoot}", 12 | "runtimeExecutable": null, 13 | "runtimeArgs": [ 14 | "--nolazy" 15 | ], 16 | "env": { 17 | "NODE_ENV": "development" 18 | }, 19 | "externalConsole": false, 20 | "sourceMaps": true, 21 | "outDir": null 22 | }, 23 | { 24 | "name": "Attach", 25 | "type": "node", 26 | "request": "attach", 27 | "port": 5858, 28 | "address": "localhost", 29 | "restart": false, 30 | "sourceMaps": false, 31 | "outDir": null, 32 | "localRoot": "${workspaceRoot}", 33 | "remoteRoot": null 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Controls the rendering size of tabs in characters. If set to auto, the value will be guessed based on the opened file. 3 | "editor.tabSize": 4, 4 | 5 | // Controls if the editor will insert spaces for tabs. If set to auto, the value will be guessed based on the opened file. 6 | "editor.insertSpaces": true, 7 | 8 | // Controls after how many characters the editor will wrap to the next line. Setting this to 0 turns on viewport width wrapping 9 | "editor.wordWrap": "wordWrapColumn", 10 | "editor.wordWrapColumn": 140, 11 | "editor.wrappingIndent": "none", 12 | 13 | "files.exclude": { 14 | "**/*.js": { "when": "$(basename).ts"}, 15 | "**/.git": true, 16 | "**/coverage": true, 17 | "**/.nyc_output": true 18 | }, 19 | // The folders to exclude when doing a full text search in the workspace. 20 | "search.exclude": { 21 | ".git": true, 22 | ".tscache": true, 23 | "bower_components": true, 24 | "bin": true, 25 | "build": true, 26 | "lib": true, 27 | "node_modules": true, 28 | "coverage": true, 29 | ".nyc_output": true 30 | }, 31 | 32 | // Always use project's provided typescript compiler version 33 | "typescript.tsdk": "node_modules/typescript/lib" 34 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "isShellCommand": true, 6 | "linux": { "command": "sh", "args": ["-c"] }, 7 | "osx": { "command": "sh", "args": ["-c"] }, 8 | "windows": { "command": "powershell", "args": ["-Command"] }, 9 | "showOutput": "silent", 10 | "problemMatcher": "$tsc", 11 | "tasks": [ 12 | { 13 | "taskName": "build", 14 | "args": ["npm run compile"], 15 | "suppressTaskName": true, 16 | "isBuildCommand": true, 17 | "isTestCommand": false, 18 | "problemMatcher": "$tsc" 19 | }, 20 | { 21 | "taskName": "tests", 22 | "args": ["npm test"], 23 | "suppressTaskName": true, 24 | "isBuildCommand": false, 25 | "isTestCommand": true 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.wotanrc.yaml: -------------------------------------------------------------------------------- 1 | extends: 2 | - wotan:recommended 3 | - ./rules 4 | rules: 5 | tcc/no-unused: error 6 | no-useless-assertion: off 7 | no-useless-predicate: off 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Klaus Meinhardt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](http://img.shields.io/npm/v/tslint-consistent-codestyle.svg)](https://npmjs.org/package/tslint-consistent-codestyle) 2 | [![Downloads](http://img.shields.io/npm/dm/tslint-consistent-codestyle.svg)](https://npmjs.org/package/tslint-consistent-codestyle) 3 | [![CircleCI](https://circleci.com/gh/ajafff/tslint-consistent-codestyle.svg?style=shield)](https://circleci.com/gh/ajafff/tslint-consistent-codestyle) 4 | [![Coverage Status](https://coveralls.io/repos/github/ajafff/tslint-consistent-codestyle/badge.svg)](https://coveralls.io/github/ajafff/tslint-consistent-codestyle) 5 | [![Join the chat at https://gitter.im/ajafff/tslint-consistent-codestyle](https://badges.gitter.im/ajafff/tslint-consistent-codestyle.svg)](https://gitter.im/ajafff/tslint-consistent-codestyle?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | [![Greenkeeper badge](https://badges.greenkeeper.io/ajafff/tslint-consistent-codestyle.svg)](https://greenkeeper.io/) 7 | 8 | # Purpose 9 | 10 | The rules in this package can be used to enforce consistent code style. 11 | 12 | # Usage 13 | 14 | Install from npm to your devDependencies (https://www.npmjs.com/package/tslint-consistent-codestyle) 15 | 16 | ```sh 17 | npm install --save-dev tslint-consistent-codestyle 18 | ``` 19 | 20 | ## With TSLint 21 | 22 | Configure tslint to use `tslint-consistent-codestyle`: 23 | 24 | This package provides an empty configuration preset that just contains the `rulesDirectory`. That means you can easily use the rules in this package, but don't get any predefined configuration. To use it, just add it to the `extends` array in your `tslint.json`: 25 | 26 | ```javascript 27 | { 28 | "extends": ["tslint-consistent-codestyle"] 29 | "rules": { 30 | ... 31 | } 32 | } 33 | ``` 34 | 35 | As of `tslint@5.2.0` you can also use `tslint-consistent-codestyle` as `rulesDirectory`: 36 | 37 | ```javascript 38 | { 39 | "rulesDirectory": ["tslint-consistent-codestyle"] 40 | "rules": { 41 | ... 42 | } 43 | } 44 | ``` 45 | 46 | Now configure some of the new rules. 47 | 48 | ## With Wotan 49 | 50 | This package provides all rules for both TSLint and [Wotan](https://github.com/fimbullinter/wotan/blob/master/packages/wotan/README.md). 51 | 52 | To use rules from this package, add the following to your `.wotanrc.yaml` file: 53 | 54 | ```yaml 55 | extends: 56 | - tslint-consistent-codestyle # makes rules from the package available with the 'tcc/' prefix 57 | rules: # now configure the rules you want to use, remember to use the 'tcc/' prefix 58 | tcc/no-collapsible-if: error 59 | tcc/no-unused: 60 | options: 'ignore-parameters' 61 | ``` 62 | 63 | # Rules 64 | 65 | Rule | Description 66 | ---- | ---- 67 | [const-parameters](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/const-parameters.md) | Declare parameters as `const` with JsDoc `/** @const */` 68 | [early-exit](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/early-exit.md) | Recommends to use an early exit instead of a long `if` block. 69 | [ext-curly](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/ext-curly.md) | Enforces where to consistently use curly braces where not strictly necessary. 70 | [naming-convention](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/naming-convention.md) | Fine grained configuration to enforce consistent naming for almost everything. E.g. variables, functions, classes, methods, parameters, enums, etc. 71 | [no-as-type-assertion](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-as-type-assertion.md) | Prefer `foo` over `foo as Type`. 72 | [no-accessor-recursion](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-accessor-recursion.md) | Don't use `get foo() { return this.foo; }`. This is most likely a typo. 73 | [no-collapsible-if](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-collapsible-if.md) | Identifies nested if statements that can be combined into one. 74 | [no-else-after-return](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-else-after-return.md) | Like [no-else-return from eslint](http://eslint.org/docs/rules/no-else-return). 75 | [no-return-undefined](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-return-undefined.md) | Just `return;` instead of `return undefined;`. 76 | [no-static-this](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-static-this.md) | Ban the use of `this` in static methods. 77 | [no-unnecessary-else](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-unnecessary-else.md) | Like `no-else-after-return` but better. 78 | [no-unnecessary-type-annotation](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-unnecessary-type-annotation.md) | Finds type annotations that can safely be removed. 79 | [no-unused](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-unused.md) | Find dead code and unused declarations. 80 | [no-var-before-return](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/no-var-before-return.md) | Checks if the returned variable is declared right before the `return` statement. 81 | [object-shorthand-properties-first](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/object-shorthand-properties-first.md) | Shorthand properties should precede regular properties. 82 | [parameter-properties](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/parameter-properties.md) | Configure how and where to declare parameter properties. 83 | [prefer-const-enum](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/prefer-const-enum.md) | Prefer `const enum` where possible. 84 | [prefer-while](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/prefer-while.md) | Prefer a `while` loop instead of a `for` loop without initializer and incrementer. 85 | -------------------------------------------------------------------------------- /docs/const-parameters.md: -------------------------------------------------------------------------------- 1 | ## const-parameters 2 | 3 | Disallows reassigning parameters that are considered constants. This rule is similar to tslint's [`no-parameter-reassignent`](https://palantir.github.io/tslint/rules/no-parameter-reassignment/) but allows you to explicitly declare which parameter is a constant with JsDoc `/** @const */` 4 | 5 | ```ts 6 | function fn(/**@const*/foo, bar) { 7 | foo++; // error on this line 8 | bar++; // no error 9 | } 10 | 11 | class C { 12 | constructor(/** @const */ public foo) { 13 | foo++; // error on this line 14 | this.foo++; // no error on this line, because only parameters are checked by this rule 15 | } 16 | } 17 | 18 | function fn(/**@constant*/foo) { // also works with @constant tag 19 | foo++; // error on this line 20 | } 21 | 22 | function fn({ // also works with destructured parameters 23 | /**@const*/ foo, 24 | baz: /**@const*/ bar, 25 | }) { 26 | foo++; // error on this line 27 | bar++; // error on this line 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/early-exit.md: -------------------------------------------------------------------------------- 1 | ## early-exit 2 | 3 | Recommends to use an early exit instead of a long `if` block. 4 | This means to `return` early from a function, `continue` early in loop, or `break` early in a switch case. 5 | 6 | So instead of: 7 | 8 | ```ts 9 | function f() { 10 | if (so) { 11 | lots; 12 | of; 13 | code; 14 | } 15 | } 16 | ``` 17 | 18 | Prefer: 19 | 20 | ```ts 21 | function f() { 22 | if (!so) return; 23 | lots; 24 | of; 25 | code; 26 | } 27 | ``` 28 | 29 | This rule also warns for if-else blocks where one branch is large and the other is a single line. 30 | So instead of: 31 | 32 | ```ts 33 | for (const x of xs) { 34 | if (so) { 35 | simple; 36 | } else { 37 | lots; 38 | of; 39 | code; 40 | } 41 | } 42 | ``` 43 | 44 | Prefer: 45 | 46 | ```ts 47 | for (const x of xs) { 48 | if (so) { 49 | simple; 50 | continue; 51 | } 52 | 53 | lots; 54 | of; 55 | code; 56 | } 57 | ``` 58 | 59 | An options object as in `"early-exit": [true, { "max-length": 4, "ignore-constructor": true }]` may be provided. 60 | 61 | `"max-length"` configures what makes a block count as "large". The default is 2 lines. 62 | 63 | If you set `"max-length"` to `0`, the rule will always suggest an early return, regardless of the line count. For example: 64 | 65 | ```ts 66 | // instead of 67 | function f() { 68 | if (so) { 69 | singleLine(); 70 | } 71 | } 72 | // prefer 73 | function f() { 74 | if (!so) return; 75 | singleLine(); 76 | } 77 | 78 | // and instead of 79 | for (const x of xs) { 80 | if (so) { 81 | foo(); 82 | } else { 83 | bar(); 84 | } 85 | } 86 | // prefer 87 | for (const x of xs) { 88 | if (so) { 89 | foo(); 90 | continue; 91 | } 92 | bar(); 93 | } 94 | ``` 95 | 96 | In addition you can choose to ignore `if` statements within class constructors with `"ignore-constructor": true` which defaults to `false`. 97 | Enabling this option prevents potential mistakes by overriding the constructed object with a non-primitive return value. 98 | -------------------------------------------------------------------------------- /docs/ext-curly.md: -------------------------------------------------------------------------------- 1 | ## ext-curly 2 | 3 | Enforces where to consistently use curly braces where not strictly necessary. If you only want `"always"` or `"as-needed"`, use the `curly` rule of tslint core instead. 4 | 5 | This rule defaults to never allow curly bracen when not necessary. 6 | 7 | Unnecessary braces can be automatically fixed. 8 | 9 | ### Options 10 | 11 | There are several options that let you specify when you want to use curly braces for consistency. All options can be used together. 12 | 13 | #### `"consistent"` 14 | 15 | Enforces curly braces on *all* branches of an `if-else` when one branch needs braces. 16 | 17 | ```ts 18 | // Not Passing 19 | if (foo) { 20 | if (bar) 21 | foo(bar) 22 | } else 23 | baz(); // the if then branch requires braces, therefore the else branch also needs them 24 | 25 | if (foo) foo() // this branch needs to get braces 26 | else if (bar) bar() // this branch needs to get braces 27 | else {} 28 | 29 | // Passing 30 | if (foo) 31 | foo() 32 | 33 | if (foo) 34 | foo() 35 | else 36 | bar() 37 | 38 | if (foo) 39 | foo() 40 | else if (bar) 41 | bar() 42 | else 43 | baz() 44 | ``` 45 | 46 | #### `"else"` 47 | 48 | Enforces curly braces on all branches of an `if` statement if it contains an else statement. This option is a superset of `"consistent"`. 49 | 50 | ```ts 51 | // Not passing 52 | if (foo) foo() 53 | else bar() // both branches need to get braces 54 | 55 | // Passing 56 | if (foo) 57 | foo() 58 | 59 | ``` 60 | 61 | #### `"braced-child"` 62 | 63 | Enforces curly braces if the child statement has or needs curly braces. That includes `try...catch`, `switch`, `while` or `for` loops and `if` statements where at least one branch has curly braces. 64 | 65 | ```ts 66 | // Not passing 67 | if (foo) // if statement needs braces 68 | switch (foo.bar) { 69 | default: 70 | } 71 | 72 | while (true) // while statement needs braces 73 | try { 74 | doStuff() 75 | } catch (e) {} 76 | 77 | for (;;) // for statement needs braces 78 | if (foo) { 79 | foo(); 80 | bar(); 81 | } else 82 | baz(); 83 | ``` 84 | 85 | #### `"nested-if-else"` 86 | 87 | Enforces curly braces when the nested statement is an `if` statement with an `else` branch. 88 | 89 | ```ts 90 | // Not passing 91 | for (;;) // for statement needs braces 92 | if (foo) 93 | foo(); 94 | else 95 | baz(); 96 | ``` -------------------------------------------------------------------------------- /docs/no-accessor-recursion.md: -------------------------------------------------------------------------------- 1 | ## no-accessor-recursion 2 | 3 | This rule warns about accessing a property inside an accessor with the same name. 4 | This is most likely a typo which causes infinite recursion. You probably meant to access a private property instead. 5 | 6 | ```ts 7 | // Not passing 8 | let obj = { 9 | get foo() { 10 | return this.foo; 11 | }, 12 | set foo(v) { 13 | this.foo = v; 14 | } 15 | } 16 | 17 | // Passing 18 | let obj = { 19 | get foo() { 20 | return this._foo; 21 | }, 22 | set foo(v) { 23 | this._foo = v; 24 | }, 25 | get bar() { 26 | return that.bar; 27 | }, 28 | set bar(v) { 29 | that.bar = v; 30 | } 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/no-as-type-assertion.md: -------------------------------------------------------------------------------- 1 | ## no-as-type-assertion 2 | 3 | The complete opposite of the tslint core rule `no-angle-bracket-type-assertion` plus the ability to automatically fix all findings. 4 | 5 | This can not be applied to `.tsx` and `.jsx` files because the `` syntax is handled as JSX opening element. 6 | 7 | ```ts 8 | // Not passing 9 | let foo = bar as number; 10 | let baz = bar as any as string; 11 | 12 | // Passing 13 | let foo = bar; 14 | let baz = bar; 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/no-collapsible-if.md: -------------------------------------------------------------------------------- 1 | ## no-collapsible-if 2 | 3 | Identifies nested if statements that can be combined into one. 4 | 5 | Also use [`no-unnecessary-else`](./no-unnecessary-else.md) to further reduce block nesting. 6 | 7 | Not passing: 8 | 9 | ```ts 10 | /* 1 */ 11 | if (foo) 12 | if (bar); 13 | /* 2 */ 14 | if (foo) { 15 | if (bar) { 16 | } 17 | } 18 | /* 3 */ 19 | if (foo) { 20 | } else { 21 | if (bar) { 22 | } else { 23 | } 24 | } 25 | ``` 26 | 27 | Passing: 28 | 29 | ```ts 30 | /* 1 */ 31 | if (foo && bar); 32 | /* 2 */ 33 | if (foo && bar) { 34 | } 35 | /* 3 */ 36 | if (foo) { 37 | } else if (bar) { 38 | } else { 39 | } 40 | 41 | if (foo) { 42 | if (bar) { 43 | } else { 44 | } 45 | } 46 | 47 | if (foo) { 48 | if (bar) { 49 | } 50 | } else { 51 | } 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/no-else-after-return.md: -------------------------------------------------------------------------------- 1 | ## no-else-after-return 2 | 3 | Works like [no-else-return from eslint](http://eslint.org/docs/rules/no-else-return). This rule can automatically fix errors. 4 | 5 | > If an if block contains a return statement, the else block becomes unnecessary. Its contents can be placed outside of the block. 6 | 7 | If you like this rule, I recommend you try [`no-unnecessary-else`](./no-unnecessary-else.md) for some bonus features. 8 | 9 | ### Options 10 | 11 | #### `"allow-else-if"` 12 | 13 | Example config: 14 | 15 | ```js 16 | "no-else-after-return": { 17 | "options": "allow-else-if" 18 | } 19 | 20 | // or 21 | 22 | "no-else-after-return": [true, "allow-else-if"] 23 | ``` 24 | 25 | Enable this option if you prefer `else if` blocks after `return` statements: 26 | 27 | ```js 28 | if (condition) { 29 | return 'foo'; 30 | } else if (otherCondition) { // this is allowed with the option 31 | return 'bar'; 32 | } 33 | 34 | if (condition) { 35 | return 'foo'; 36 | } else if (otherCondition) { 37 | return 'bar'; 38 | } else { // this is still not allowed with the option 39 | return 'baz'; 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/no-return-undefined.md: -------------------------------------------------------------------------------- 1 | ## no-return-undefined 2 | 3 | Using `return undefined` or `return void 0;` is unnecessary, because `undefined` is the default return value. Just use `return;` instead. 4 | 5 | ### Options 6 | 7 | #### `"allow-void-expression"` 8 | 9 | Allows the use of void expressions like `return void callback()` or `return void (foo = bar)`. 10 | Using the void keyword with a static expression like `return void 0` is still not allowed, as this serves no real purpose. 11 | -------------------------------------------------------------------------------- /docs/no-static-this.md: -------------------------------------------------------------------------------- 1 | ## no-static-this 2 | 3 | Ban the use of `this` in static methods. 4 | 5 | __Rationale:__ It's pretty hard to wrap your head around the meaning of `this` in a static context. Especially newcomers will not recognise, that you are in fact referencing the `class` (or the `constructor` to be more precise). 6 | -------------------------------------------------------------------------------- /docs/no-unnecessary-else.md: -------------------------------------------------------------------------------- 1 | ## no-unnecessary-else 2 | 3 | If an if block contains a return, throw, continue or break statement, the else block becomes unnecessary. Its contents can be placed outside of the block. This rule can automatically fix errors. 4 | 5 | This is basically [no-else-after-return](./no-else-after-return.md) with additional checks including `throw`, `continue` and `break`. Replace `no-else-after-return` with `no-unnecessary-else` in your `tslint.json`. 6 | -------------------------------------------------------------------------------- /docs/no-unnecessary-type-annotation.md: -------------------------------------------------------------------------------- 1 | ## no-unnecessary-type-annotation 2 | 3 | Finds type annotations of variables and parameters that can be removed because the compiler can infer the type. 4 | 5 | **Known limitation:** sometimes removing a type annotation can cause circular inference and therefore a compiler error. 6 | 7 | **Failing:** 8 | 9 | ```ts 10 | const foo: 1 = 1; 11 | const foo: number = 1; 12 | let foo: number = 1; 13 | const arr: string[] = ["foo", "bar"]; 14 | 15 | const myVerboseSignature: (a: number) => number = function(a: number): number { 16 | return a; 17 | } 18 | 19 | declare function takeCallback(cb: (a: string) => void): void; 20 | takeCallback((a: string) => a); 21 | ``` 22 | 23 | ### Options 24 | 25 | #### `"check-return-type"` 26 | 27 | Enables checking function return types. This option can cause false positives for (mutually) recursive functions. 28 | -------------------------------------------------------------------------------- /docs/no-unused.md: -------------------------------------------------------------------------------- 1 | ## no-unused 2 | 3 | Disallows unused imports, variables, functions, classes, type parameters and more. Similar to `tsc`’s `--noUnusedParameters` and `--noUnusedLocals` options and the `tslint` core rule `no-unused-variable`. 4 | 5 | Use `no-unused-expression` in addition to this rule to uncover even more dead code. 6 | 7 | ### Differences to `--noUnusedParameters` and `--noUnusedLocals` 8 | 9 | * Errors can be disabled and don't fail compilation. 10 | 11 | ### Differences to `no-unused-variable` 12 | 13 | * Works without the type checker and therefore without `--project` option. 14 | * Works for .js files. 15 | * No false positives with parameters of abstract methods. 16 | * No false positives with destructuring. See [tslint#2876](https://github.com/palantir/tslint/issues/2876) 17 | * No side effects on typescript's type system. See [tslint#2736](https://github.com/palantir/tslint/issues/2736) [tslint#2649](https://github.com/palantir/tslint/issues/2649) [tslint#2571](https://github.com/palantir/tslint/issues/2571) 18 | * Only fixes unused names of function and class expressions. 19 | * Cannot check if an import is implicitly used by the declaration emitter, but you can disable errors on imports in .ts files with `"ignore-imports"` 20 | 21 | ### Differences to both 22 | 23 | * Can optionally complain about named function and class expressions that are never used by name with options `"unused-function-expression-name"` and `"unused-class-expression-name"` 24 | * Can optionally complain about unused catch bindings (supported since typescript@2.5.1) with option `"unused-catch-binding"` 25 | * Does not check private class members. 26 | * Does not check for unused labels. 27 | * Needs to be more liberal with variables in global scope, e.g. top level variable declarations if the file has no imports or exports. 28 | * Flags write only variables as error. (Also supported by typescript@2.6.0) 29 | * Flags functions and classes that are only used inside of their declaration as error. 30 | * Handles declarations in different domains separately: 31 | 32 | ```ts 33 | interface T {} 34 | // ~ [Interface 'T' is unused.] 35 | class T {} 36 | export = new T(); 37 | 38 | interface U {} 39 | namespace U {} 40 | // ~ [Namespace 'U' is unused.] 41 | export let v: U; 42 | ``` 43 | 44 | ### Ignoring uninteresting parameters and variables 45 | 46 | There are cases where you simply need to have a parameter or variable, but don't use it. You can prefix the name with an underscore `_` to ignore it. 47 | 48 | The underscore works in the following cases: 49 | 50 | 1. Parameters 51 | 2. Object destructuring that contains rest 52 | 3. `for ... in` and `for ... of` loops 53 | 54 | ```ts 55 | /* 1 */ 56 | function doStuff(_a, _b, c) { 57 | return c; 58 | } 59 | 60 | /* 2 */ 61 | let {a: _a, ...bAndC} = {a: 1, b: 2, c: 3}; 62 | 63 | /* 3 */ 64 | for (const _ in someObj) 65 | ++keyCount; 66 | for (const _ of someArr) 67 | ++valueCount; 68 | ``` 69 | 70 | ### Options 71 | 72 | #### `"ignore-imports"` 73 | 74 | When using `--declaration` in your `tsconfig.json`, the declaration emitter may implicitly use otherwise unused imports (See [TypeScript#9944](https://github.com/Microsoft/TypeScript/issues/9944)). You can either disable the error on those imports or use the `"ignore-imports"` option to ignore imports in all .ts files completely. 75 | 76 | #### `"ignore-parameters"` 77 | 78 | Disable errors on unused parameters. This does not include destructured parameters. 79 | 80 | #### `"unused-function-expression-name"` 81 | 82 | *Enables* checking for named function expressions that are never used by name. 83 | 84 | These names may serve a purpose in your code, e.g. for easier debugging. Therefore this option is not enabled by default. 85 | 86 | Not Passing: 87 | 88 | ```ts 89 | setTimeout(function foo() { }, 100); 90 | // ~~~ [Function 'foo' is never used by its name. Convert it to an anonymous function expression.] 91 | ``` 92 | 93 | Passing: 94 | 95 | ```ts 96 | setTimeout(function() { }, 100); 97 | 98 | let result = (function fac(i) { 99 | return i === 1 ? 1 : i * fac(i - 1); 100 | })(5); 101 | ``` 102 | 103 | #### `"unused-class-expression-name"` 104 | 105 | Basically the same as `"unused-function-expression-name"` but for class expressions. 106 | 107 | #### `"unused-catch-binding"` 108 | 109 | As of TypeScript@2.5.1 you can omit the catch binding if you're not going to use it. This option helps you identify such cases. 110 | 111 | Not Passing: 112 | 113 | ```ts 114 | try { 115 | JSON.parse(foo); 116 | } catch (e) { 117 | // ~ [Variable 'e' is unused.] 118 | console.log('invalid json'); 119 | } 120 | ``` 121 | 122 | Passing: 123 | 124 | ```ts 125 | try { 126 | JSON.parse(foo); 127 | } catch { 128 | console.log('invalid json'); 129 | } 130 | ``` 131 | -------------------------------------------------------------------------------- /docs/no-var-before-return.md: -------------------------------------------------------------------------------- 1 | ## no-var-before-return 2 | 3 | Checks if the returned variable is declared right before the `return` statement. 4 | 5 | > Declaring a variable only to immediately return it is a **bad practice**. Some developers argue that the practice improves code readability, because it enables them to explicitly name what is being returned. However, this variable is an internal implementation detail that is not exposed to the callers of the method. **The method name should be sufficient for callers to know exactly what will be returned.** 6 | 7 | ### Options 8 | 9 | #### `"allow-destructuring"` 10 | 11 | Allows destructuring a variable immediately before returning it. 12 | -------------------------------------------------------------------------------- /docs/object-shorthand-properties-first.md: -------------------------------------------------------------------------------- 1 | ## object-shorthand-properties-first 2 | 3 | By convention and for better readability, shorthand properties should precede regular property declarations. 4 | 5 | Not passing: 6 | 7 | ```ts 8 | let obj = { 9 | foo: foo, 10 | bar, 11 | baz: baz, 12 | }; 13 | ``` 14 | 15 | Passing: 16 | 17 | ```ts 18 | let obj = { 19 | bar, 20 | foo: foo, 21 | baz: baz, 22 | }; 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/parameter-properties.md: -------------------------------------------------------------------------------- 1 | ## parameter-properties 2 | 3 | Configure how and where to declare parameter properties. 4 | 5 | ### Options 6 | 7 | All rule options are optional, but without any option this rule does nothing. 8 | 9 | Usage: 10 | 11 | ```js 12 | "parameter-properties": [ 13 | true, 14 | "all-or-none", // forces all or none of a constructors parameters to be parameter properties 15 | "leading", // forces parameter properties to precede regular parameters 16 | "trailing", // forces regular parameters to precede parameter properties 17 | "member-access", // forces an access modifier for every parameter property 18 | "readonly" // forces all parameter properties to be readonly 19 | ] 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/prefer-const-enum.md: -------------------------------------------------------------------------------- 1 | ## prefer-const-enum 2 | 3 | An enum that is never dynamically accessed, can be declared as `const enum`. 4 | Const enums are replaced with their number or string values during compilation. 5 | That can yield a significant performance increase for heavily used enums. 6 | This rule identifies enums that could be const enums and can automatically fix them. 7 | 8 | Enums that are exported or available in the global scope are ignored. 9 | -------------------------------------------------------------------------------- /docs/prefer-while.md: -------------------------------------------------------------------------------- 1 | ## prefer-while 2 | 3 | A `for`-loop without initializer and incrementer can also be rewritten as `while`-loop. This rule can automatically fix errors. 4 | 5 | :warning: As of TSLint v5.10.0 this rule is a TSLint core rule. You will no longer be able to use the rule from this package. 6 | 7 | ```ts 8 | for (;;) 9 | doStuff(); 10 | 11 | for (;condition;) 12 | doOtherStuff(); 13 | 14 | // Prefer 15 | while (true) 16 | doStuff(); 17 | 18 | while (condition) 19 | doOtherStuff(); 20 | ``` 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tslint-consistent-codestyle", 3 | "version": "1.16.0", 4 | "description": "Additional rules to enforce constistent code style with tslint", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/ajafff/tslint-consistent-codestyle" 8 | }, 9 | "main": "rules/index.js", 10 | "scripts": { 11 | "test": "tslint --test test/rules/*/*", 12 | "precompile": "rimraf '{src,rules}/*.js'", 13 | "compile": "tsc -p .", 14 | "prepublishOnly": "npm run verify", 15 | "lint:wotan": "wotan", 16 | "lint:tslint": "wotan -m @fimbul/valtyr", 17 | "lint": "run-p lint:*", 18 | "verify": "run-s compile lint coverage", 19 | "precoverage": "rimraf coverage .nyc_output", 20 | "coverage": "nyc npm test", 21 | "report-coverage": "cat ./coverage/lcov.info | coveralls", 22 | "postpublish": "git push origin master --tags && npm run github-release", 23 | "github-release": "GITHUB_TOKEN=$(cat ~/github_token.txt) github-release-from-changelog" 24 | }, 25 | "keywords": [ 26 | "tslint", 27 | "custom-rules", 28 | "rules", 29 | "stylish", 30 | "lint", 31 | "linting", 32 | "linter", 33 | "tslint-plugin" 34 | ], 35 | "license": "MIT", 36 | "dependencies": { 37 | "@fimbul/bifrost": "^0.21.0", 38 | "tslib": "^1.7.1", 39 | "tsutils": "^2.29.0" 40 | }, 41 | "devDependencies": { 42 | "@fimbul/mithotyn": "^0.21.0", 43 | "@fimbul/valtyr": "^0.21.0", 44 | "@fimbul/wotan": "^0.21.1", 45 | "coveralls": "^3.0.0", 46 | "github-release-from-changelog": "^2.0.0", 47 | "npm-run-all": "^4.1.1", 48 | "nyc": "^13.2.0", 49 | "rimraf": "^3.0.0", 50 | "tslint": "^5.8.0", 51 | "typescript": "3.3" 52 | }, 53 | "peerDependencies": { 54 | "tslint": "^5.0.0", 55 | "typescript": ">=2.1.4 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >=3.1.0-dev || >=3.2.0-dev || >=3.3.0-dev || >=3.4.0-dev" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /rules/constParametersRule.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2017 Palantir Technologies, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import { collectVariableUsage, getDeclarationOfBindingElement, isReassignmentTarget, getJsDoc, parseJsDocOfNode } from 'tsutils'; 19 | import * as ts from 'typescript'; 20 | import * as Lint from 'tslint'; 21 | 22 | export class Rule extends Lint.Rules.AbstractRule { 23 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 24 | return this.applyWithFunction(sourceFile, walk); 25 | } 26 | } 27 | 28 | function walk(ctx: Lint.WalkContext): void { 29 | collectVariableUsage(ctx.sourceFile).forEach((variable, identifier) => { 30 | if (!isParameter(identifier.parent!) || !isConst(identifier, ctx.sourceFile)) 31 | return; 32 | for (const use of variable.uses) 33 | if (isReassignmentTarget(use.location)) 34 | ctx.addFailureAtNode(use.location, `Cannot reassign constant parameter '${identifier.text}'.`); 35 | }); 36 | } 37 | 38 | function isParameter(node: ts.Node): boolean { 39 | switch (node.kind) { 40 | case ts.SyntaxKind.Parameter: 41 | return true; 42 | case ts.SyntaxKind.BindingElement: 43 | return getDeclarationOfBindingElement(node).kind === ts.SyntaxKind.Parameter; 44 | default: 45 | return false; 46 | } 47 | } 48 | 49 | function isConst(name: ts.Identifier, sourceFile: ts.SourceFile) { 50 | if (name.parent!.kind === ts.SyntaxKind.Parameter) 51 | return getJsDoc(name.parent!, sourceFile).some(jsDocContainsConst); 52 | // destructuring 53 | return parseJsDocOfNode(name, true, sourceFile).some(jsDocContainsConst); 54 | } 55 | 56 | function jsDocContainsConst(jsDoc: ts.JSDoc): boolean { 57 | if (jsDoc.tags !== undefined) 58 | for (const tag of jsDoc.tags) 59 | if (tag.tagName.text === 'const' || tag.tagName.text === 'constant') 60 | return true; 61 | return false; 62 | } 63 | -------------------------------------------------------------------------------- /rules/earlyExitRule.ts: -------------------------------------------------------------------------------- 1 | import { isBlock, isCaseOrDefaultClause, isIfStatement, isFunctionScopeBoundary } from 'tsutils'; 2 | import * as Lint from 'tslint'; 3 | import * as ts from 'typescript'; 4 | 5 | export class Rule extends Lint.Rules.AbstractRule { 6 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 7 | const options = { 8 | 'max-length': 2, 9 | 'ignore-constructor': false, 10 | ...this.ruleArguments[0], 11 | }; 12 | return this.applyWithFunction(sourceFile, walk, options); 13 | } 14 | } 15 | 16 | function failureString(exit: string): string { 17 | return `Remainder of block is inside 'if' statement. Prefer to invert the condition and '${exit}' early.`; 18 | } 19 | 20 | function failureStringSmall(exit: string, branch: 'else' | 'then'): string { 21 | return `'${branch}' branch is small; prefer an early '${exit}' to a full if-else.`; 22 | } 23 | 24 | function failureStringAlways(exit: string): string { 25 | return `Prefer an early '${exit}' to a full if-else.`; 26 | } 27 | 28 | interface IOptions { 29 | 'max-length': number; 30 | 'ignore-constructor': boolean; 31 | } 32 | 33 | function walk(ctx: Lint.WalkContext) { 34 | const { 35 | sourceFile, 36 | options: { 'max-length': maxLineLength, 'ignore-constructor': ignoreConstructor }, 37 | } = ctx; 38 | 39 | return ts.forEachChild(sourceFile, function cb(node): void { 40 | if (isIfStatement(node) && (!ignoreConstructor || !isConstructorClosestFunctionScopeBoundary(node))) 41 | check(node); 42 | 43 | return ts.forEachChild(node, cb); 44 | }); 45 | 46 | function check(node: ts.IfStatement): void { 47 | const exit = getExit(node); 48 | if (exit === undefined) 49 | return; 50 | 51 | const { thenStatement, elseStatement } = node; 52 | const thenSize = size(thenStatement, sourceFile); 53 | 54 | if (elseStatement === undefined) { 55 | if (isLarge(thenSize)) 56 | fail(failureString(exit)); 57 | return; 58 | } 59 | 60 | // Never fail if there's an `else if`. 61 | if (elseStatement.kind === ts.SyntaxKind.IfStatement) 62 | return; 63 | 64 | if (maxLineLength === 0) 65 | return fail(failureStringAlways(exit)); 66 | 67 | const elseSize = size(elseStatement, sourceFile); 68 | 69 | if (isSmall(thenSize) && isLarge(elseSize)) { 70 | fail(failureStringSmall(exit, 'then')); 71 | } else if (isSmall(elseSize) && isLarge(thenSize)) { 72 | fail(failureStringSmall(exit, 'else')); 73 | } 74 | 75 | function fail(failure: string) { 76 | ctx.addFailureAt(node.getStart(sourceFile), 2, failure); 77 | } 78 | } 79 | 80 | function isSmall(length: number): boolean { 81 | return length === 1; 82 | } 83 | 84 | function isLarge(length: number): boolean { 85 | return length > maxLineLength; 86 | } 87 | } 88 | 89 | function size(node: ts.Node, sourceFile: ts.SourceFile): number { 90 | return isBlock(node) 91 | ? node.statements.length === 0 ? 0 : diff(node.statements[0].getStart(sourceFile), node.statements.end, sourceFile) 92 | : diff(node.getStart(sourceFile), node.end, sourceFile); 93 | } 94 | 95 | function diff(start: number, end: number, sourceFile: ts.SourceFile): number { 96 | return ts.getLineAndCharacterOfPosition(sourceFile, end).line 97 | - ts.getLineAndCharacterOfPosition(sourceFile, start).line 98 | + 1; 99 | } 100 | 101 | function getExit(node: ts.IfStatement): string | undefined { 102 | const parent = node.parent!; 103 | if (isBlock(parent)) { 104 | const container = parent.parent!; 105 | return isCaseOrDefaultClause(container) && container.statements.length === 1 106 | ? getCaseClauseExit(container, parent, node) 107 | // Must be the last statement in the block 108 | : isLastStatement(node, parent.statements) ? getEarlyExitKind(container) : undefined; 109 | } 110 | return isCaseOrDefaultClause(parent) 111 | ? getCaseClauseExit(parent, parent, node) 112 | // This is the only statement in its container, so of course it's the final statement. 113 | : getEarlyExitKind(parent); 114 | } 115 | 116 | function getCaseClauseExit( 117 | clause: ts.CaseOrDefaultClause, 118 | { statements }: ts.CaseOrDefaultClause | ts.Block, 119 | node: ts.IfStatement): string | undefined { 120 | return statements[statements.length - 1].kind === ts.SyntaxKind.BreakStatement 121 | // Must be the last node before the break statement 122 | ? isLastStatement(node, statements, statements.length - 2) ? 'break' : undefined 123 | // If no 'break' statement, this is a fallthrough, unless we're at the last clause. 124 | : clause.parent!.clauses[clause.parent!.clauses.length - 1] === clause && isLastStatement(node, statements) ? 'break' : undefined; 125 | } 126 | 127 | function getEarlyExitKind({ kind }: ts.Node): string | undefined { 128 | switch (kind) { 129 | case ts.SyntaxKind.FunctionDeclaration: 130 | case ts.SyntaxKind.FunctionExpression: 131 | case ts.SyntaxKind.ArrowFunction: 132 | case ts.SyntaxKind.MethodDeclaration: 133 | case ts.SyntaxKind.Constructor: 134 | case ts.SyntaxKind.GetAccessor: 135 | case ts.SyntaxKind.SetAccessor: 136 | return 'return'; 137 | 138 | case ts.SyntaxKind.ForInStatement: 139 | case ts.SyntaxKind.ForOfStatement: 140 | case ts.SyntaxKind.ForStatement: 141 | case ts.SyntaxKind.WhileStatement: 142 | case ts.SyntaxKind.DoStatement: 143 | return 'continue'; 144 | 145 | default: 146 | // At any other location, we can't use an early exit. 147 | // (TODO: maybe we could, but we would need more control flow information here.) 148 | // (Cause clauses handled separately.) 149 | return; 150 | } 151 | } 152 | 153 | function isLastStatement(ifStatement: ts.IfStatement, statements: ReadonlyArray, i: number = statements.length - 1): boolean { 154 | while (true) { 155 | const statement = statements[i]; 156 | if (statement === ifStatement) 157 | return true; 158 | if (statement.kind !== ts.SyntaxKind.FunctionDeclaration) 159 | return false; 160 | if (i === 0) 161 | // ifStatement should have been in statements 162 | throw new Error(); 163 | i--; 164 | } 165 | } 166 | 167 | function isConstructorClosestFunctionScopeBoundary(node: ts.Node): boolean { 168 | let currentParent = node.parent; 169 | while (currentParent) { 170 | if (isFunctionScopeBoundary(currentParent)) 171 | return currentParent.kind === ts.SyntaxKind.Constructor; 172 | currentParent = currentParent.parent; 173 | } 174 | return false; 175 | } 176 | -------------------------------------------------------------------------------- /rules/extCurlyRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import { isBlock, isIterationStatement, isIfStatement, getNextToken, isSameLine, isLabeledStatement } from 'tsutils'; 4 | import { isElseIf } from '../src/utils'; 5 | 6 | const FAIL_MESSAGE_MISSING = `statement must be braced`; 7 | const FAIL_MESSAGE_UNNECESSARY = `unnecessary curly braces`; 8 | 9 | const OPTION_ELSE = 'else'; 10 | const OPTION_CONSISTENT = 'consistent'; 11 | const OPTION_BRACED_CHILD = 'braced-child'; 12 | const OPTION_NESTED_IF_ELSE = 'nested-if-else'; 13 | 14 | interface IOptions { 15 | else: boolean; 16 | consistent: boolean; 17 | child: boolean; 18 | nestedIfElse: boolean; 19 | } 20 | 21 | export class Rule extends Lint.Rules.AbstractRule { 22 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 23 | return this.applyWithWalker(new ExtCurlyWalker(sourceFile, this.ruleName, { 24 | else: this.ruleArguments.indexOf(OPTION_ELSE) !== -1, 25 | consistent: this.ruleArguments.indexOf(OPTION_CONSISTENT) !== -1, 26 | child: this.ruleArguments.indexOf(OPTION_BRACED_CHILD) !== -1, 27 | nestedIfElse: this.ruleArguments.indexOf(OPTION_NESTED_IF_ELSE) !== -1, 28 | })); 29 | } 30 | } 31 | 32 | class ExtCurlyWalker extends Lint.AbstractWalker { 33 | public walk(sourceFile: ts.SourceFile) { 34 | const cb = (node: ts.Node): void => { 35 | if (isIterationStatement(node)) { 36 | this._checkLoop(node); 37 | } else if (isIfStatement(node)) { 38 | this._checkIfStatement(node); 39 | } 40 | return ts.forEachChild(node, cb); 41 | }; 42 | return ts.forEachChild(sourceFile, cb); 43 | } 44 | 45 | private _checkLoop(node: ts.IterationStatement) { 46 | if (this._needsBraces(node.statement)) { 47 | if (node.statement.kind !== ts.SyntaxKind.Block) 48 | this.addFailureAtNode(node.statement, FAIL_MESSAGE_MISSING); 49 | } else if (node.statement.kind === ts.SyntaxKind.Block) { 50 | this._reportUnnecessary(node.statement); 51 | } 52 | } 53 | 54 | private _checkIfStatement(node: ts.IfStatement) { 55 | const [then, otherwise] = this._ifStatementNeedsBraces(node); 56 | if (then) { 57 | if (node.thenStatement.kind !== ts.SyntaxKind.Block) 58 | this.addFailureAtNode(node.thenStatement, FAIL_MESSAGE_MISSING); 59 | } else if (node.thenStatement.kind === ts.SyntaxKind.Block) { 60 | this._reportUnnecessary(node.thenStatement); 61 | } 62 | if (otherwise) { 63 | if (node.elseStatement !== undefined && 64 | node.elseStatement.kind !== ts.SyntaxKind.Block && node.elseStatement.kind !== ts.SyntaxKind.IfStatement) 65 | this.addFailureAtNode(node.elseStatement, FAIL_MESSAGE_MISSING); 66 | } else if (node.elseStatement !== undefined && node.elseStatement.kind === ts.SyntaxKind.Block) { 67 | this._reportUnnecessary(node.elseStatement); 68 | } 69 | } 70 | 71 | private _needsBraces(node: ts.Statement, allowIfElse?: boolean): boolean { 72 | if (isBlock(node)) 73 | return node.statements.length !== 1 || this._needsBraces(node.statements[0], allowIfElse); 74 | if (!allowIfElse && this.options.nestedIfElse && isIfStatement(node) && node.elseStatement !== undefined) 75 | return true; 76 | if (!this.options.child) 77 | return false; 78 | if (isIfStatement(node)) { 79 | const result = this._ifStatementNeedsBraces(node); 80 | return result[0] || result[1]; 81 | } 82 | if (isIterationStatement(node) || isLabeledStatement(node)) 83 | return this._needsBraces(node.statement); 84 | return node.kind === ts.SyntaxKind.SwitchStatement || node.kind === ts.SyntaxKind.TryStatement; 85 | } 86 | 87 | private _ifStatementNeedsBraces(node: ts.IfStatement, excludeElse?: boolean): [boolean, boolean] { 88 | if (this.options.else) { 89 | if (node.elseStatement !== undefined || isElseIf(node)) 90 | return [true, true]; 91 | } else if (this.options.consistent) { 92 | if (this._needsBraces(node.thenStatement) || 93 | !excludeElse && node.elseStatement !== undefined && 94 | (isIfStatement(node.elseStatement) 95 | ? this._ifStatementNeedsBraces(node.elseStatement)[0] 96 | : this._needsBraces(node.elseStatement, true))) 97 | return [true, true]; 98 | if (isElseIf(node) && this._ifStatementNeedsBraces(node.parent, true)[0]) 99 | return [true, true]; 100 | } 101 | if (node.elseStatement !== undefined) { 102 | const statement = unwrapBlock(node.thenStatement); 103 | return [ 104 | isIfStatement(statement) && statement.elseStatement === undefined || this._needsBraces(statement), 105 | !excludeElse && this._needsBraces(node.elseStatement, true), 106 | ]; 107 | } 108 | return [this._needsBraces(node.thenStatement), false]; 109 | } 110 | 111 | private _reportUnnecessary(block: ts.Block) { 112 | const closeBrace = block.getChildAt(2, this.sourceFile); 113 | const nextTokenStart = getNextToken(closeBrace, this.sourceFile)!.getStart(this.sourceFile); 114 | const closeFix = isSameLine(this.sourceFile, closeBrace.end, nextTokenStart) 115 | ? Lint.Replacement.deleteFromTo(closeBrace.end - 1, nextTokenStart) 116 | : Lint.Replacement.deleteFromTo(block.statements.end, block.end); 117 | this.addFailure(block.statements.pos - 1, block.end, FAIL_MESSAGE_UNNECESSARY, [ 118 | Lint.Replacement.deleteFromTo(block.pos, block.statements.pos), 119 | closeFix, 120 | ]); 121 | } 122 | } 123 | 124 | function unwrapBlock(node: ts.Statement): ts.Statement { 125 | while (isBlock(node) && node.statements.length === 1) 126 | node = node.statements[0]; 127 | return node; 128 | } 129 | -------------------------------------------------------------------------------- /rules/index.ts: -------------------------------------------------------------------------------- 1 | // this file exists for tslint to resolve the rules directory 2 | export = { 3 | rulesDirectory: '.', // for TSLint 4 | rulesDirectories: { // for Wotan 5 | tcc: './wotan', 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /rules/noAccessorRecursionRule.ts: -------------------------------------------------------------------------------- 1 | import * as Lint from 'tslint'; 2 | import * as ts from 'typescript'; 3 | import { isAccessorDeclaration, getPropertyName, hasOwnThisReference, isPropertyAccessExpression } from 'tsutils'; 4 | 5 | const FAILURE_STRING = 'accessor recursion is not allowed'; 6 | 7 | export class Rule extends Lint.Rules.AbstractRule { 8 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 9 | return this.applyWithFunction(sourceFile, walk); 10 | } 11 | } 12 | 13 | function walk(ctx: Lint.WalkContext) { 14 | let name: string | undefined; 15 | 16 | return ctx.sourceFile.statements.forEach(function cb(node: ts.Node): void { 17 | if (isAccessorDeclaration(node) && node.body !== undefined) { 18 | const before = name; 19 | name = getPropertyName(node.name); 20 | node.body.statements.forEach(cb); 21 | name = before; 22 | } else if (name !== undefined && hasOwnThisReference(node)) { 23 | const before = name; 24 | name = undefined; 25 | ts.forEachChild(node, cb); 26 | name = before; 27 | } else if (name !== undefined && isPropertyAccessExpression(node) && 28 | node.expression.kind === ts.SyntaxKind.ThisKeyword && node.name.text === name) { 29 | ctx.addFailureAtNode(node, FAILURE_STRING); 30 | } else { 31 | return ts.forEachChild(node, cb); 32 | } 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /rules/noAsTypeAssertionRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import {isAsExpression} from 'tsutils'; 4 | 5 | const FAIL_MESSAGE = 'use instead of `as Type`'; 6 | 7 | export class Rule extends Lint.Rules.AbstractRule { 8 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 9 | return this.applyWithFunction(sourceFile, walk); 10 | } 11 | } 12 | 13 | function walk(ctx: Lint.WalkContext) { 14 | if (ctx.sourceFile.languageVariant === ts.LanguageVariant.JSX) 15 | return; 16 | return ts.forEachChild(ctx.sourceFile, function cb(node): void { 17 | if (isAsExpression(node)) { 18 | let {type, expression} = node; 19 | let replacement = `<${type.getText(ctx.sourceFile)}>`; 20 | while (isAsExpression(expression)) { 21 | ({type, expression} = expression); 22 | replacement += `<${type.getText(ctx.sourceFile)}>`; 23 | } 24 | ctx.addFailure(type.pos - 2, node.end, FAIL_MESSAGE, [ 25 | Lint.Replacement.appendText(expression.getStart(ctx.sourceFile), replacement), 26 | Lint.Replacement.deleteFromTo(expression.end, node.end), 27 | ]); 28 | return cb(expression); 29 | } 30 | return ts.forEachChild(node, cb); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /rules/noCollapsibleIfRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import * as utils from 'tsutils'; 4 | 5 | import { AbstractIfStatementWalker } from '../src/walker'; 6 | 7 | const FAIL_MERGE_IF = `if statements can be merged`; 8 | const FAIL_MERGE_ELSE_IF = `if statement can be merged with previous else`; 9 | 10 | export class Rule extends Lint.Rules.AbstractRule { 11 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 12 | return this.applyWithWalker(new CollapsibleIfWalker(sourceFile, this.ruleName, undefined)); 13 | } 14 | } 15 | 16 | class CollapsibleIfWalker extends AbstractIfStatementWalker { 17 | protected _checkIfStatement(node: ts.IfStatement) { 18 | if (node.elseStatement === undefined) { 19 | let then = node.thenStatement; 20 | if (utils.isBlockLike(then) && then.statements.length === 1) 21 | then = then.statements[0]; 22 | if (utils.isIfStatement(then) && then.elseStatement === undefined) 23 | this.addFailure(node.getStart(this.sourceFile), then.thenStatement.pos, FAIL_MERGE_IF); 24 | } else if (utils.isBlock(node.elseStatement) && 25 | node.elseStatement.statements.length === 1 && 26 | utils.isIfStatement(node.elseStatement.statements[0])) { 27 | this.addFailure( 28 | node.elseStatement.pos - 4, 29 | (node.elseStatement.statements[0]).thenStatement.pos, 30 | FAIL_MERGE_ELSE_IF, 31 | ); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rules/noElseAfterReturnRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | import { isElseIf } from '../src/utils'; 5 | import { AbstractIfStatementWalker } from '../src/walker'; 6 | import { isIfStatement, getControlFlowEnd, isReturnStatement } from 'tsutils'; 7 | 8 | const FAIL_MESSAGE = `unnecessary else after return`; 9 | const OPTION_ALLOW_ELSE_IF = 'allow-else-if'; 10 | 11 | interface IOptions { 12 | allowElseIf: boolean; 13 | } 14 | 15 | export class Rule extends Lint.Rules.AbstractRule { 16 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 17 | return this.applyWithWalker(new IfWalker(sourceFile, this.ruleName, { 18 | allowElseIf: this.ruleArguments.indexOf(OPTION_ALLOW_ELSE_IF) !== -1, 19 | })); 20 | } 21 | } 22 | 23 | class IfWalker extends AbstractIfStatementWalker { 24 | protected _checkIfStatement(node: ts.IfStatement) { 25 | if (shouldCheckNode(node, this.options.allowElseIf) && endsWithReturnStatement(node.thenStatement)) 26 | this._reportUnnecessaryElse(node.elseStatement, FAIL_MESSAGE); 27 | } 28 | } 29 | 30 | function shouldCheckNode(node: ts.IfStatement, allowElseIf: boolean): node is ts.IfStatement & {elseStatement: {}} { 31 | if (node.elseStatement === undefined) 32 | return false; 33 | if (!allowElseIf) 34 | return !isElseIf(node); 35 | if (isIfStatement(node.elseStatement) && isElseIf(node.elseStatement)) 36 | return false; 37 | while (isElseIf(node)) { 38 | node = node.parent; 39 | if (!endsWithReturnStatement(node.thenStatement)) 40 | return false; 41 | } 42 | return true; 43 | } 44 | 45 | function endsWithReturnStatement(node: ts.Statement): boolean { 46 | const end = getControlFlowEnd(node); 47 | return end.end && end.statements.every(isReturnStatement); 48 | } 49 | -------------------------------------------------------------------------------- /rules/noReturnUndefinedRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import * as utils from 'tsutils'; 4 | 5 | import {isUndefined} from '../src/utils'; 6 | import {AbstractReturnStatementWalker} from '../src/walker'; 7 | 8 | const FAIL_MESSAGE = `don't return explicit undefined`; 9 | const ALLOW_VOID_EXPRESSION_OPTION = 'allow-void-expression'; 10 | 11 | interface IOptions { 12 | allowVoid: boolean; 13 | } 14 | 15 | export class Rule extends Lint.Rules.AbstractRule { 16 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 17 | return this.applyWithWalker(new ReturnWalker(sourceFile, this.ruleName, { 18 | allowVoid: this.ruleArguments.indexOf(ALLOW_VOID_EXPRESSION_OPTION) !== -1, 19 | })); 20 | } 21 | } 22 | 23 | class ReturnWalker extends AbstractReturnStatementWalker { 24 | protected _checkReturnStatement(node: ts.ReturnStatement) { 25 | if (node.expression !== undefined && this._isUndefined(node.expression)) 26 | this.addFailureAtNode(node.expression, FAIL_MESSAGE); 27 | } 28 | 29 | private _isUndefined(expression: ts.Expression): boolean { 30 | return this.options.allowVoid ? isUndefinedNotVoidExpr(expression) : isUndefined(expression); 31 | } 32 | } 33 | 34 | function isUndefinedNotVoidExpr(expression: ts.Expression): boolean { 35 | if (utils.isIdentifier(expression) && expression.text === 'undefined') 36 | return true; 37 | return utils.isVoidExpression(expression) && utils.isLiteralExpression(expression.expression); 38 | } 39 | -------------------------------------------------------------------------------- /rules/noStaticThisRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import * as utils from 'tsutils'; 4 | 5 | const FAIL_MESSAGE = `don't use this in static methods`; 6 | 7 | export class Rule extends Lint.Rules.AbstractRule { 8 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 9 | return this.applyWithFunction(sourceFile, walk); 10 | } 11 | } 12 | 13 | function walk(ctx: Lint.WalkContext) { 14 | const stack: boolean[] = []; 15 | let current = false; 16 | const cb = (child: ts.Node) => { 17 | const boundary = utils.isScopeBoundary(child); 18 | if (boundary) { 19 | stack.push(current); 20 | if (!current || utils.hasOwnThisReference(child)) 21 | current = isStatic(child); 22 | } 23 | if (current && child.kind === ts.SyntaxKind.ThisKeyword) 24 | ctx.addFailureAtNode(child, FAIL_MESSAGE); 25 | ts.forEachChild(child, cb); 26 | if (boundary) 27 | current = stack.pop()!; 28 | }; 29 | return ts.forEachChild(ctx.sourceFile, cb); 30 | } 31 | 32 | function isStatic(node: ts.Node): boolean { 33 | return (node.kind === ts.SyntaxKind.MethodDeclaration || 34 | node.kind === ts.SyntaxKind.GetAccessor || 35 | node.kind === ts.SyntaxKind.SetAccessor) && 36 | utils.hasModifier(node.modifiers, ts.SyntaxKind.StaticKeyword); 37 | } 38 | -------------------------------------------------------------------------------- /rules/noUnnecessaryElseRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import { AbstractIfStatementWalker } from '../src/walker'; 4 | import { isElseIf } from '../src/utils'; 5 | import { endsControlFlow } from 'tsutils'; 6 | 7 | const FAIL_MESSAGE = `unnecessary else`; 8 | 9 | export class Rule extends Lint.Rules.AbstractRule { 10 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 11 | return this.applyWithWalker(new IfWalker(sourceFile, this.ruleName, undefined)); 12 | } 13 | } 14 | 15 | class IfWalker extends AbstractIfStatementWalker { 16 | protected _checkIfStatement(node: ts.IfStatement): void { 17 | const {elseStatement} = node; 18 | if (elseStatement !== undefined && !isElseIf(node) && endsControlFlow(node.thenStatement)) 19 | this._reportUnnecessaryElse(elseStatement, FAIL_MESSAGE); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/noVarBeforeReturnRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import { collectVariableUsage, VariableInfo, isReturnStatement, isVariableStatement, isIdentifier, isLiteralExpression} from 'tsutils'; 4 | 5 | import { isUndefined } from '../src/utils'; 6 | 7 | const OPTION_ALLOW_DESTRUCTURING = 'allow-destructuring'; 8 | 9 | interface IOptions { 10 | allowDestructuring: boolean; 11 | } 12 | 13 | export class Rule extends Lint.Rules.AbstractRule { 14 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 15 | return this.applyWithFunction(sourceFile, walk, { 16 | allowDestructuring: this.ruleArguments.indexOf(OPTION_ALLOW_DESTRUCTURING) !== -1, 17 | }); 18 | } 19 | } 20 | 21 | function walk(ctx: Lint.WalkContext) { 22 | let variables: Map | undefined; 23 | return ts.forEachChild(ctx.sourceFile, cbNode, cbNodeArray); 24 | 25 | function isUnused(node: ts.Identifier): boolean { 26 | if (variables === undefined) 27 | variables = collectVariableUsage(ctx.sourceFile); 28 | return variables.get(node)!.uses.length === 1; 29 | } 30 | 31 | function cbNode(node: ts.Node): void { 32 | return ts.forEachChild(node, cbNode, cbNodeArray); 33 | } 34 | 35 | function cbNodeArray(nodes: ReadonlyArray): void { 36 | if (nodes.length === 0) 37 | return; 38 | ts.forEachChild(nodes[0], cbNode, cbNodeArray); 39 | for (let i = 1; i < nodes.length; ++i) { 40 | const node = nodes[i]; 41 | if (isReturnStatement(node)) { 42 | if (node.expression === undefined) 43 | continue; 44 | if (!isIdentifier(node.expression)) { 45 | ts.forEachChild(node.expression, cbNode, cbNodeArray); 46 | continue; 47 | } 48 | const previous = nodes[i - 1]; 49 | if (isVariableStatement(previous) && declaresVariable(previous, node.expression.text, isUnused, ctx.options)) 50 | ctx.addFailureAtNode(node.expression, `don't declare variable ${node.expression.text} to return it immediately`); 51 | } else { 52 | ts.forEachChild(node, cbNode, cbNodeArray); 53 | } 54 | } 55 | } 56 | } 57 | 58 | function declaresVariable( 59 | statement: ts.VariableStatement, 60 | name: string, 61 | isUnused: (node: ts.Identifier) => boolean, 62 | options: IOptions, 63 | ): boolean { 64 | const declarations = statement.declarationList.declarations; 65 | const lastDeclaration = declarations[declarations.length - 1].name; 66 | if (lastDeclaration.kind === ts.SyntaxKind.Identifier) 67 | return lastDeclaration.text === name && isUnused(lastDeclaration); 68 | return !options.allowDestructuring && isSimpleDestructuringForName(lastDeclaration, name, isUnused); 69 | } 70 | 71 | function isSimpleDestructuringForName(pattern: ts.BindingPattern, name: string, isUnused: (node: ts.Identifier) => boolean): boolean { 72 | const identifiersSeen = new Set(); 73 | let inArray = 0; 74 | let dependsOnVar = 0; 75 | 76 | return recur(pattern) === true; 77 | 78 | function recur(p: ts.BindingPattern): boolean | undefined { 79 | if (p.kind === ts.SyntaxKind.ArrayBindingPattern) { 80 | ++inArray; 81 | for (const element of p.elements) { 82 | if (element.kind !== ts.SyntaxKind.OmittedExpression) { 83 | const result = handleBindingElement(element); 84 | if (result !== undefined) 85 | return result; 86 | } 87 | } 88 | --inArray; 89 | } else { 90 | for (const element of p.elements) { 91 | const result = handleBindingElement(element); 92 | if (result !== undefined) 93 | return result; 94 | } 95 | } 96 | } 97 | function handleBindingElement(element: ts.BindingElement): boolean | undefined { 98 | if (element.name.kind !== ts.SyntaxKind.Identifier) { 99 | if (dependsOnPrevious(element)) { 100 | ++dependsOnVar; 101 | const result = recur(element.name); 102 | --dependsOnVar; 103 | return result; 104 | } 105 | return recur(element.name); 106 | } 107 | if (element.name.text !== name) 108 | return void identifiersSeen.add(element.name.text); 109 | if (dependsOnVar !== 0) 110 | return false; 111 | if (element.dotDotDotToken) { 112 | if (element.parent!.elements.length > 1 || 113 | inArray > (element.parent!.kind === ts.SyntaxKind.ArrayBindingPattern ? 1 : 0)) 114 | return false; 115 | } else if (inArray !== 0) { 116 | return false; 117 | } 118 | if (element.initializer !== undefined && !isUndefined(element.initializer)) 119 | return false; 120 | return !dependsOnPrevious(element) && isUnused(element.name); 121 | } 122 | function dependsOnPrevious(element: ts.BindingElement): boolean { 123 | if (element.propertyName === undefined || element.propertyName.kind !== ts.SyntaxKind.ComputedPropertyName) 124 | return false; 125 | if (isIdentifier(element.propertyName.expression)) 126 | return identifiersSeen.has(element.propertyName.expression.text); 127 | if (isLiteralExpression(element.propertyName.expression)) 128 | return false; 129 | return true; // TODO implement better check for expressions 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /rules/objectShorthandPropertiesFirstRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | const FAIL_MESSAGE = `shorthand properties should come first`; 5 | 6 | export class Rule extends Lint.Rules.AbstractRule { 7 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 8 | return this.applyWithWalker(new ObjectWalker(sourceFile, this.ruleName, undefined)); 9 | } 10 | } 11 | 12 | class ObjectWalker extends Lint.AbstractWalker { 13 | public walk(sourceFile: ts.SourceFile) { 14 | const cb = (node: ts.Node): void => { 15 | if (node.kind === ts.SyntaxKind.ObjectLiteralExpression) 16 | this._checkObjectLiteral(node); 17 | return ts.forEachChild(node, cb); 18 | }; 19 | return ts.forEachChild(sourceFile, cb); 20 | } 21 | private _checkObjectLiteral(node: ts.ObjectLiteralExpression) { 22 | let seenRegularProperty = false; 23 | for (const property of node.properties) { 24 | if (property.kind === ts.SyntaxKind.PropertyAssignment) { 25 | seenRegularProperty = true; 26 | } else if (property.kind === ts.SyntaxKind.SpreadAssignment) { 27 | // reset at spread, because ordering matters 28 | seenRegularProperty = false; 29 | } else if (seenRegularProperty && property.kind === ts.SyntaxKind.ShorthandPropertyAssignment) { 30 | this.addFailureAtNode(property, FAIL_MESSAGE); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rules/oddnessCheckRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import * as utils from 'tsutils'; 4 | 5 | const FAILURE_STRING = 'Modulus 2 can be replaced with & 1'; 6 | 7 | export class Rule extends Lint.Rules.AbstractRule { 8 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 9 | return this.applyWithWalker(new ReturnWalker(sourceFile, this.ruleName, undefined)); 10 | } 11 | } 12 | 13 | class ReturnWalker extends Lint.AbstractWalker { 14 | public walk(sourceFile: ts.SourceFile) { 15 | const cb = (node: ts.Node): void => { 16 | if (utils.isBinaryExpression(node) && 17 | node.operatorToken.kind === ts.SyntaxKind.PercentToken && 18 | utils.isNumericLiteral(node.right) && 19 | node.right.text === '2') { 20 | // TODO if this is part of a comparison with a negative value, this failure would be a false positive 21 | const start = node.operatorToken.getStart(sourceFile); 22 | this.addFailure(start, node.right.end, FAILURE_STRING, [ 23 | new Lint.Replacement(start, 1, '&'), 24 | new Lint.Replacement(node.right.end - 1, 1, '1'), 25 | ]); 26 | } 27 | return ts.forEachChild(node, cb); 28 | }; 29 | return ts.forEachChild(sourceFile, cb); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rules/parameterPropertiesRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import * as utils from 'tsutils'; 4 | 5 | import { AbstractConfigDependentRule } from '../src/rules'; 6 | 7 | const ALL_OR_NONE_OPTION = 'all-or-none'; 8 | const LEADING_OPTION = 'leading'; 9 | const TRAILING_OPTION = 'trailing'; 10 | const READONLY_OPTION = 'readonly'; 11 | const MEMBER_ACCESS_OPTION = 'member-access'; 12 | 13 | const ALL_OR_NONE_FAIL = 'don\'t mix parameter properties with regular parameters'; 14 | const LEADING_FAIL = 'parameter properties must precede regular parameters'; 15 | const TRAILING_FAIL = 'regular parameters must precede parameter properties'; 16 | const READONLY_FAIL = 'parameter property must be readonly'; 17 | const MEMBER_ACCESS_FAIL = 'parameter property must have access modifier'; 18 | 19 | // TODO 20 | // - no parameter use 21 | // - no reassign 22 | // - no reassign parameter 23 | // - no reassign property 24 | // - no reassign readonly 25 | // - no reassign readonly parameter 26 | // - no reassign readonly property 27 | 28 | interface IOptions { 29 | allOrNone: boolean; 30 | leading: boolean; 31 | trailing: boolean; 32 | readOnly: boolean; 33 | memberAccess: boolean; 34 | } 35 | 36 | export class Rule extends AbstractConfigDependentRule { 37 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 38 | return this.applyWithWalker(new ParameterPropertyWalker(sourceFile, this.ruleName, { 39 | allOrNone: this.ruleArguments.indexOf(ALL_OR_NONE_OPTION) !== -1, 40 | leading: this.ruleArguments.indexOf(LEADING_OPTION) !== -1, 41 | trailing: this.ruleArguments.indexOf(TRAILING_OPTION) !== -1, 42 | readOnly: this.ruleArguments.indexOf(READONLY_OPTION) !== -1, 43 | memberAccess: this.ruleArguments.indexOf(MEMBER_ACCESS_OPTION) !== -1, 44 | })); 45 | } 46 | } 47 | 48 | class ParameterPropertyWalker extends Lint.AbstractWalker { 49 | public walk(sourceFile: ts.SourceFile) { 50 | const cb = (node: ts.Node): void => { 51 | if (node.kind === ts.SyntaxKind.Constructor) 52 | this._checkConstructorDeclaration(node); 53 | return ts.forEachChild(node, cb); 54 | }; 55 | return ts.forEachChild(sourceFile, cb); 56 | } 57 | 58 | private _checkConstructorDeclaration(node: ts.ConstructorDeclaration) { 59 | const parameters = node.parameters; 60 | const length = parameters.length; 61 | if (length === 0) 62 | return; 63 | 64 | let index = -1; 65 | for (let i = 0; i < length; ++i) { 66 | if (utils.isParameterProperty(parameters[i])) { 67 | index = i; 68 | break; 69 | } 70 | } 71 | if (index === -1) 72 | return; 73 | 74 | if (this.options.allOrNone) { 75 | const start = parameters[0].getStart(this.getSourceFile()); 76 | const end = parameters[parameters.length - 1].getEnd(); 77 | if (index > 0) { 78 | this.addFailure(start, end, ALL_OR_NONE_FAIL); 79 | } else { 80 | for (let i = index + 1; i < length; ++i) { 81 | if (!utils.isParameterProperty(parameters[i])) { 82 | this.addFailure(start, end, ALL_OR_NONE_FAIL); 83 | break; 84 | } 85 | } 86 | } 87 | } else if (this.options.leading) { 88 | let regular = index > 0; 89 | for (let i = index; i < length; ++i) { 90 | if (utils.isParameterProperty(parameters[i])) { 91 | if (regular) 92 | this.addFailureAtNode(parameters[i], LEADING_FAIL); 93 | } else { 94 | regular = true; 95 | } 96 | } 97 | } else if (this.options.trailing) { 98 | for (let i = index; i < length; ++i) 99 | if (!utils.isParameterProperty(parameters[i])) 100 | this.addFailureAtNode(parameters[i], TRAILING_FAIL); 101 | } 102 | 103 | if (this.options.memberAccess) { 104 | for (let i = index; i < length; ++i) { 105 | const parameter = parameters[i]; 106 | if (utils.isParameterProperty(parameter) && !utils.hasAccessModifier(parameter)) 107 | this.addFailureAtNode(parameter, MEMBER_ACCESS_FAIL); 108 | } 109 | } 110 | 111 | if (this.options.readOnly) { 112 | for (let i = index; i < length; ++i) { 113 | const parameter = parameters[i]; 114 | if (utils.isParameterProperty(parameter) && !utils.hasModifier(parameter.modifiers, ts.SyntaxKind.ReadonlyKeyword)) 115 | this.addFailureAtNode(parameter, READONLY_FAIL); 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /rules/preferWhileRule.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | 4 | const FAIL_MESSAGE = 'Prefer `while` loops instead of `for` loops without an initializer and incrementor.'; 5 | 6 | export class Rule extends Lint.Rules.AbstractRule { 7 | public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { 8 | return this.applyWithWalker(new ForWalker(sourceFile, this.ruleName, undefined)); 9 | } 10 | } 11 | 12 | class ForWalker extends Lint.AbstractWalker { 13 | public walk(sourceFile: ts.SourceFile) { 14 | const cb = (node: ts.Node): void => { 15 | if (node.kind === ts.SyntaxKind.ForStatement) 16 | this._checkForStatement(node); 17 | return ts.forEachChild(node, cb); 18 | }; 19 | return ts.forEachChild(sourceFile, cb); 20 | } 21 | private _checkForStatement(node: ts.ForStatement) { 22 | if (node.initializer === undefined && node.incrementor === undefined) { 23 | const start = node.getStart(this.sourceFile); 24 | const closeParenEnd = node.statement.pos; 25 | let fix: Lint.Fix; 26 | if (node.condition === undefined) { 27 | fix = Lint.Replacement.replaceFromTo(start, closeParenEnd, 'while (true)'); 28 | } else { 29 | fix = [ 30 | Lint.Replacement.replaceFromTo(start, node.condition.getStart(this.sourceFile), 'while ('), 31 | Lint.Replacement.deleteFromTo(node.condition.end, closeParenEnd - 1), 32 | ]; 33 | } 34 | 35 | this.addFailure(start, closeParenEnd, FAIL_MESSAGE, fix); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rules/wotan/const-parameters.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../constParametersRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/early-exit.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../earlyExitRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/ext-curly.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../extCurlyRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/naming-convention.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../namingConventionRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/no-accessor-recursion.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../noAccessorRecursionRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/no-as-type-assertion.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../noAsTypeAssertionRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/no-collapsible-if.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../noCollapsibleIfRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/no-else-after-return.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../noElseAfterReturnRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/no-return-undefined.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../noReturnUndefinedRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/no-static-this.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../noStaticThisRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/no-unnecessary-else.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../noUnnecessaryElseRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/no-unnecessary-type-annotation.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../noUnnecessaryTypeAnnotationRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/no-unused.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../noUnusedRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/no-var-before-return.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../noVarBeforeReturnRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/object-shorthand-properties-first.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../objectShorthandPropertiesFirstRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/parameter-properties.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../parameterPropertiesRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/prefer-const-enum.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../preferConstEnumRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /rules/wotan/prefer-while.ts: -------------------------------------------------------------------------------- 1 | import { wrapTslintRule } from '@fimbul/bifrost'; 2 | import { Rule } from '../preferWhileRule'; 3 | 4 | const rule = wrapTslintRule(Rule); 5 | export {rule as Rule}; 6 | -------------------------------------------------------------------------------- /src/rules.ts: -------------------------------------------------------------------------------- 1 | import * as Lint from 'tslint'; 2 | 3 | export abstract class AbstractConfigDependentRule extends Lint.Rules.AbstractRule { 4 | public isEnabled(): boolean { 5 | return super.isEnabled() && this.ruleArguments.length !== 0; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as utils from 'tsutils'; 3 | 4 | export function isUndefined(expression: ts.Expression): boolean { 5 | return utils.isIdentifier(expression) && expression.text === 'undefined' || 6 | expression.kind === ts.SyntaxKind.VoidExpression; 7 | } 8 | 9 | export function isElseIf(node: ts.IfStatement): node is ts.IfStatement & { parent: ts.IfStatement & { elseStatement: ts.IfStatement } } { 10 | const parent = node.parent!; 11 | return utils.isIfStatement(parent) && 12 | parent.elseStatement === node; 13 | } 14 | -------------------------------------------------------------------------------- /src/walker.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as Lint from 'tslint'; 3 | import { isBlock, isBlockScopedVariableDeclarationList } from 'tsutils'; 4 | 5 | export abstract class AbstractReturnStatementWalker extends Lint.AbstractWalker { 6 | public walk(sourceFile: ts.SourceFile) { 7 | const cb = (node: ts.Node): void => { 8 | if (node.kind === ts.SyntaxKind.ReturnStatement) 9 | this._checkReturnStatement(node); 10 | return ts.forEachChild(node, cb); 11 | }; 12 | return ts.forEachChild(sourceFile, cb); 13 | } 14 | 15 | protected abstract _checkReturnStatement(node: ts.ReturnStatement): void; 16 | } 17 | 18 | export abstract class AbstractIfStatementWalker extends Lint.AbstractWalker { 19 | public walk(sourceFile: ts.SourceFile) { 20 | const cb = (node: ts.Node): void => { 21 | if (node.kind === ts.SyntaxKind.IfStatement) 22 | this._checkIfStatement(node); 23 | return ts.forEachChild(node, cb); 24 | }; 25 | return ts.forEachChild(sourceFile, cb); 26 | } 27 | 28 | protected _reportUnnecessaryElse(elseStatement: ts.Statement, message: string) { 29 | const elseKeyword = elseStatement.parent!.getChildAt(5 /*else*/, this.sourceFile); 30 | if (isBlock(elseStatement) && !elseStatement.statements.some(isBlockScopedDeclaration)) { 31 | // remove else scope if safe: keep blocks where local variables change scope when unwrapped 32 | this.addFailureAtNode(elseKeyword, message, [ 33 | Lint.Replacement.deleteFromTo(elseKeyword.end - 4, elseStatement.statements.pos), // removes `else {` 34 | Lint.Replacement.deleteText(elseStatement.end - 1, 1), // removes `}` 35 | ]); 36 | } else { 37 | // remove else only 38 | this.addFailureAtNode(elseKeyword, message, Lint.Replacement.deleteText(elseKeyword.end - 4, 4)); 39 | } 40 | } 41 | 42 | protected abstract _checkIfStatement(node: ts.IfStatement): void; 43 | } 44 | 45 | // TODO replace with isBlockScopedDeclarationStatement from tsutils@3.7.0 46 | function isBlockScopedDeclaration(statement: ts.Statement): boolean { 47 | switch (statement.kind) { 48 | case ts.SyntaxKind.VariableStatement: 49 | return isBlockScopedVariableDeclarationList((statement).declarationList); 50 | case ts.SyntaxKind.ClassDeclaration: 51 | case ts.SyntaxKind.EnumDeclaration: 52 | case ts.SyntaxKind.InterfaceDeclaration: 53 | case ts.SyntaxKind.TypeAliasDeclaration: 54 | return true; 55 | default: return false; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/rules/const-parameters/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | function fn(foo) { 2 | foo++; 3 | } 4 | function fn(/**@constant*/foo) { 5 | foo++; 6 | ~~~ [err % ('foo')] 7 | } 8 | function fn(/** @const */ foo, bar) { 9 | foo++; 10 | ~~~ [err % ('foo')] 11 | bar++; 12 | } 13 | function fn(/** @const */ foo, bar) { 14 | bar = foo; 15 | } 16 | function fn(/**@const some text*/foo) { 17 | foo++; 18 | ~~~ [err % ('foo')] 19 | } 20 | function fn({/**@const*/foo}) { 21 | foo++; 22 | ~~~ [err % ('foo')] 23 | } 24 | function fn({bar: /**@const*/foo}) { 25 | foo++; 26 | ~~~ [err % ('foo')] 27 | } 28 | function fn({/**@const*/foo, bar}) { 29 | foo++; 30 | ~~~ [err % ('foo')] 31 | bar++; 32 | } 33 | class Clazz { 34 | constructor(/**@const*/foo, /**@const*/ private bar, /**@const*/ readonly baz) { 35 | foo++; 36 | ~~~ [err % ('foo')] 37 | bar++; 38 | ~~~ [err % ('bar')] 39 | baz++; 40 | ~~~ [err % ('baz')] 41 | this.bar++; 42 | this.baz++; 43 | } 44 | } 45 | function fn( 46 | /** some description */ 47 | /** @constant */ 48 | foo, 49 | ) { 50 | foo++; 51 | ~~~ [err % ('foo')] 52 | } 53 | function fn(/** foo */foo) { 54 | foo++; 55 | } 56 | function fn(/**const*/foo) { 57 | foo++; 58 | } 59 | function fn(/**@param*/foo) { 60 | foo++; 61 | } 62 | 63 | [err]: Cannot reassign constant parameter '%s'. 64 | -------------------------------------------------------------------------------- /test/rules/const-parameters/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "const-parameters": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/early-exit/always/test-if-else.ts.lint: -------------------------------------------------------------------------------- 1 | // Tests for 'if-else' statements. 2 | 3 | function f() { 4 | if (so) 5 | ~~ [ALWAYS] 6 | 1; 7 | else { 8 | 1; 9 | 2; 10 | 3; 11 | } 12 | } 13 | 14 | function f() { 15 | if (so) { 16 | ~~ [ALWAYS] 17 | 1; 18 | 2; 19 | 3; 20 | } else 21 | 1; 22 | } 23 | 24 | function f() { 25 | if (so) { 26 | ~~ [ALWAYS] 27 | 1; 28 | 2; 29 | 3; 30 | } else { 31 | 1; 32 | 2; 33 | 3; 34 | } 35 | } 36 | 37 | function f() { 38 | if (so) 39 | ~~ [ALWAYS] 40 | 1; 41 | else 42 | 1; 43 | } 44 | 45 | function f() { 46 | if (so) 47 | 1; 48 | else if (os) 49 | 1; 50 | } 51 | 52 | function f() { 53 | if (so) { 54 | ~~ [ALWAYS] 55 | } else { 56 | f; 57 | } 58 | } 59 | 60 | [ALWAYS]: Prefer an early 'return' to a full if-else. 61 | -------------------------------------------------------------------------------- /test/rules/early-exit/always/test.ts.lint: -------------------------------------------------------------------------------- 1 | function f() { 2 | blah; // Lines before the 'if' are irrelevant 3 | if (so) 4 | ~~ [0] 5 | two( 6 | lines); 7 | 8 | // Function declarations after the 'if' are irrelevant 9 | function inner() {} 10 | } 11 | 12 | function f() { 13 | if (so) { 14 | ~~ [0] 15 | blah(); 16 | } 17 | } 18 | 19 | function f() { 20 | if (so) {} // don't fail if there is nothing in the then block 21 | } 22 | 23 | // Won't trigger if there are any more lines after the 'if'. 24 | function f() { 25 | if (so) { 26 | two( 27 | lines); 28 | } 29 | blah(); 30 | } 31 | 32 | // Won't trigger inside blocks (only inside functions or loops). 33 | if (so) 34 | some( 35 | long, 36 | thing); 37 | else { 38 | if (so) 39 | two( 40 | lines); 41 | } 42 | 43 | function f() { 44 | if (so) 45 | ~~ [ALWAYS] 46 | two( 47 | lines); 48 | else { 49 | blah(); 50 | // Ideally it would ask for an early exit at this 'if'. 51 | // But currently we only handle 'if' inside a function or loop. 52 | if (so) 53 | blah(); 54 | else 55 | two( 56 | lines); 57 | } 58 | } 59 | 60 | [0]: Remainder of block is inside 'if' statement. Prefer to invert the condition and 'return' early. 61 | [ALWAYS]: Prefer an early 'return' to a full if-else. 62 | -------------------------------------------------------------------------------- /test/rules/early-exit/always/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "early-exit": [true, { "max-length": 0 }] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/early-exit/default/test-if-else.ts.lint: -------------------------------------------------------------------------------- 1 | // Tests for 'if-else' statements. 2 | 3 | function f() { 4 | if (so) 5 | ~~ [SMALL_THEN] 6 | 1; 7 | else { 8 | 1; 9 | 2; 10 | 3; 11 | } 12 | } 13 | 14 | function f() { 15 | if (so) { 16 | ~~ [SMALL_ELSE] 17 | 1; 18 | 2; 19 | 3; 20 | } else 21 | 1; 22 | } 23 | 24 | function f() { 25 | if (so) { 26 | 1; 27 | 2; 28 | 3; 29 | } else { 30 | 1; 31 | 2; 32 | 3; 33 | } 34 | } 35 | 36 | function f() { 37 | if (so) 38 | 1; 39 | else 40 | 1; 41 | } 42 | 43 | function f() { 44 | if (so) 45 | 1; 46 | else if (os) 47 | 1; 48 | } 49 | 50 | [SMALL_THEN]: 'then' branch is small; prefer an early 'return' to a full if-else. 51 | [SMALL_ELSE]: 'else' branch is small; prefer an early 'return' to a full if-else. 52 | -------------------------------------------------------------------------------- /test/rules/early-exit/default/test-switch.ts.lint: -------------------------------------------------------------------------------- 1 | switch (x) { 2 | case 0: 3 | if (so) 4 | ~~ [BREAK] 5 | two( 6 | lines); 7 | break; 8 | 9 | // Also works with a block clause 10 | case 0: { 11 | if (so) 12 | ~~ [BREAK] 13 | two( 14 | lines); 15 | break; 16 | } 17 | 18 | case 0: 19 | // Falls through, so can't use early return 20 | if (so) 21 | two( 22 | lines); 23 | 24 | case 0: { 25 | if (so) 26 | two( 27 | lines); 28 | } 29 | 30 | case 0: 31 | if (so) 32 | two( 33 | lines); 34 | blah(); 35 | break; 36 | 37 | case 0: { 38 | if (so) 39 | two( 40 | lines); 41 | blah(); 42 | break; 43 | } 44 | } 45 | 46 | switch (x) { 47 | case 0: 48 | // Last case, so it's not fallthrough 49 | if (so) 50 | ~~ [BREAK] 51 | two( 52 | lines); 53 | } 54 | 55 | switch (x) { 56 | case 0: { 57 | // Last case, so it's not fallthrough 58 | if (so) 59 | ~~ [BREAK] 60 | two( 61 | lines); 62 | } 63 | } 64 | 65 | switch (x) { 66 | case 0: 67 | if (so) 68 | two( 69 | lines); 70 | blah(); 71 | } 72 | 73 | switch (x) { 74 | case 0: { 75 | if (so) 76 | two( 77 | lines); 78 | blah(); 79 | } 80 | } 81 | 82 | switch (x) { 83 | case 0: 84 | if (so) 85 | ~~ [BREAK] 86 | two( 87 | lines); 88 | break; 89 | } 90 | 91 | switch (x) { 92 | case 0: { 93 | if (so) 94 | ~~ [BREAK] 95 | two( 96 | lines); 97 | break; 98 | } 99 | } 100 | 101 | switch (x) { 102 | default: 103 | if (so) 104 | ~~ [BREAK] 105 | two( 106 | lines); 107 | } 108 | 109 | switch (x) { 110 | default: { 111 | if (so) 112 | ~~ [BREAK] 113 | two( 114 | lines); 115 | } 116 | } 117 | 118 | switch (x) { 119 | default: 120 | if (so) 121 | two( 122 | lines); 123 | blah(); 124 | } 125 | 126 | switch (x) { 127 | default: { 128 | if (so) 129 | two( 130 | lines); 131 | blah(); 132 | } 133 | } 134 | 135 | [BREAK]: Remainder of block is inside 'if' statement. Prefer to invert the condition and 'break' early. 136 | -------------------------------------------------------------------------------- /test/rules/early-exit/default/test-syntax-kinds.ts.lint: -------------------------------------------------------------------------------- 1 | // Tests for all kinds of parent nodes it should work on 2 | // 'switch' cases are in test-switch 3 | 4 | function f() { 5 | if (so) 6 | ~~ [RET] 7 | two( 8 | lines); 9 | } 10 | 11 | f = function() { 12 | if (so) 13 | ~~ [RET] 14 | two( 15 | lines); 16 | } 17 | 18 | f = () => { 19 | if (so) 20 | ~~ [RET] 21 | two( 22 | lines); 23 | } 24 | 25 | class C { 26 | constructor() { 27 | if (so) 28 | ~~ [RET] 29 | two( 30 | lines); 31 | } 32 | 33 | m() { 34 | if (so) 35 | ~~ [RET] 36 | two( 37 | lines); 38 | } 39 | 40 | get x() { 41 | if (so) 42 | ~~ [RET] 43 | two( 44 | lines); 45 | } 46 | 47 | set s() { 48 | if (s) 49 | ~~ [RET] 50 | two( 51 | lines); 52 | } 53 | } 54 | 55 | for (const x in y) 56 | if (so) 57 | ~~ [CONT] 58 | two( 59 | lines); 60 | 61 | for (const x of y) 62 | if (so) 63 | ~~ [CONT] 64 | two( 65 | lines); 66 | 67 | for (let i = 0; i < 10; i++) 68 | if (so) 69 | ~~ [CONT] 70 | two( 71 | lines); 72 | 73 | while (so) 74 | if (so) 75 | ~~ [CONT] 76 | two( 77 | lines); 78 | 79 | do { 80 | if (so) 81 | ~~ [CONT] 82 | two( 83 | lines); 84 | } while (so); 85 | 86 | [CONT]: Remainder of block is inside 'if' statement. Prefer to invert the condition and 'continue' early. 87 | [RET]: Remainder of block is inside 'if' statement. Prefer to invert the condition and 'return' early. 88 | -------------------------------------------------------------------------------- /test/rules/early-exit/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | function f() { 2 | blah; // Lines before the 'if' are irrelevant 3 | if (so) 4 | ~~ [0] 5 | two( 6 | lines); 7 | 8 | // Function declarations after the 'if' are irrelevant 9 | function inner() {} 10 | } 11 | 12 | // Block size is measured from first statement to last statement -- does *not* include curly braces. 13 | // So the size here is 1, not 3. 14 | function f() { 15 | if (so) { 16 | blah(); 17 | } 18 | } 19 | 20 | // Won't trigger if there are any more lines after the 'if'. 21 | function f() { 22 | if (so) { 23 | two( 24 | lines); 25 | } 26 | blah(); 27 | } 28 | 29 | // Won't trigger inside blocks (only inside functions or loops). 30 | if (so) 31 | some( 32 | long, 33 | thing); 34 | else { 35 | if (so) 36 | two( 37 | lines); 38 | } 39 | 40 | function f() { 41 | if (so) 42 | two( 43 | lines); 44 | else { 45 | blah(); 46 | // Ideally it would ask for an early exit at this 'if'. 47 | // But currently we only handle 'if' inside a function or loop. 48 | if (so) 49 | blah(); 50 | else 51 | two( 52 | lines); 53 | } 54 | } 55 | 56 | [0]: Remainder of block is inside 'if' statement. Prefer to invert the condition and 'return' early. 57 | -------------------------------------------------------------------------------- /test/rules/early-exit/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "early-exit": [true, { "max-length": 1 }] 5 | } 6 | } -------------------------------------------------------------------------------- /test/rules/early-exit/igore-constructor/test.ts.lint: -------------------------------------------------------------------------------- 1 | class Foo { 2 | constructor() { 3 | if (so) { 4 | blah(); 5 | } 6 | } 7 | } 8 | 9 | class Foo { 10 | constructor() { 11 | if (so) { 12 | two( 13 | lines); 14 | } 15 | } 16 | } 17 | 18 | class Foo { 19 | constructor() { 20 | if (so) { 21 | some( 22 | long, 23 | thing); 24 | } 25 | } 26 | } 27 | 28 | class Foo { 29 | constructor() { 30 | function f() { 31 | if (so) { 32 | ~~ [0] 33 | some( 34 | long, 35 | thing); 36 | } 37 | 38 | function f() { 39 | if (so) { 40 | ~~ [0] 41 | some( 42 | long, 43 | thing); 44 | } 45 | } 46 | } 47 | } 48 | } 49 | 50 | // Making sure that if statement outside function scope doesn't cause error 51 | if (so) { 52 | some( 53 | long, 54 | thing); 55 | } 56 | 57 | [0]: Remainder of block is inside 'if' statement. Prefer to invert the condition and 'return' early. 58 | -------------------------------------------------------------------------------- /test/rules/early-exit/igore-constructor/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "early-exit": [true, { "ignore-constructor": true }] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/ext-curly/braced-child/test.ts.lint: -------------------------------------------------------------------------------- 1 | while (true) 2 | switch (foo) {} 3 | ~~~~~~~~~~~~~~~ [1] 4 | while (true) { 5 | switch (foo) {} 6 | } 7 | 8 | if (foo) 9 | for (;;) {} 10 | ~~~~~~~~~~~ [1] 11 | 12 | do 13 | if (foo) 14 | ~~~~~~~~ 15 | foo(); 16 | ~~~~~~~~~~~~~~ 17 | else { 18 | ~~~~~~~~~~ 19 | bar(); 20 | ~~~~~~~~~~~~~~ 21 | baz(); 22 | ~~~~~~~~~~~~~~ 23 | } 24 | ~~~~~ [1] 25 | while (true): 26 | 27 | do if (foo) foo(); else bar(); while (true); 28 | 29 | if (foo) 30 | try { doStuff(); } catch (e) {} 31 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [1] 32 | 33 | if (foo) foo(); 34 | else try { doStuff(); } finally { doMoreStuff(); } 35 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [1] 36 | 37 | if (foo) 38 | if (bar) foo(bar); else if (baz) foo(baz); else {foo();bas();} 39 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [1] 40 | 41 | if (foo) { 42 | label: while (true) {} 43 | } 44 | 45 | if (foo) 46 | label: while (true) {} 47 | ~~~~~~~~~~~~~~~~~~~~~~ [1] 48 | 49 | 50 | if (foo) 51 | label: foo: bar: while (true) {} 52 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [1] 53 | 54 | [1]: statement must be braced 55 | -------------------------------------------------------------------------------- /test/rules/ext-curly/braced-child/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "ext-curly": [true, "braced-child"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/ext-curly/consistent/test.ts.lint: -------------------------------------------------------------------------------- 1 | if (foo) { foo(); } 2 | ~~~~~~~~~~ [0] 3 | if (foo) { foo(); bar(); } 4 | else bar(); 5 | ~~~~~~ [1] 6 | 7 | if (foo) {} 8 | else if (bar) bar(); 9 | ~~~~~~ [1] 10 | if (foo) {} 11 | else if (bar) bar(); 12 | ~~~~~~ [1] 13 | else baz(); 14 | ~~~~~~ [1] 15 | 16 | if (foo) foo(); 17 | else if (bar) bar(); 18 | else baz(); 19 | 20 | if (foo) foo(); 21 | ~~~~~~ [1] 22 | else if (bar) bar(); 23 | ~~~~~~ [1] 24 | else if (baz) baz(); 25 | ~~~~~~ [1] 26 | else if (bas) bas(); 27 | ~~~~~~ [1] 28 | else { 29 | doStuff(); 30 | doMoreStuff(); 31 | } 32 | 33 | if (foo) foo(); 34 | else if (bar) bar(); 35 | else if (baz) baz(); 36 | else if (bas) bas(); 37 | else { doStuff(); } 38 | ~~~~~~~~~~~~~~ [0] 39 | 40 | if (foo) foo(); 41 | ~~~~~~ [1] 42 | else if (bar) bar(); 43 | ~~~~~~ [1] 44 | else if (baz) {} 45 | else if (bas) bas(); 46 | ~~~~~~ [1] 47 | else doStuff(); 48 | ~~~~~~~~~~ [1] 49 | 50 | [0]: unnecessary curly braces 51 | [1]: statement must be braced -------------------------------------------------------------------------------- /test/rules/ext-curly/consistent/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "ext-curly": [true, "consistent"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/ext-curly/default/test.ts.fix: -------------------------------------------------------------------------------- 1 | for (;;) {} 2 | for (let foo of bar) ; 3 | for (let key in obj)foo(); 4 | while (true) doStuff(); 5 | do { 6 | foo(); 7 | bar(); 8 | } while (true); 9 | do 10 | foo(); 11 | while (true); 12 | 13 | while (true) 14 | switch (foo) {} 15 | while (false) 16 | switch (foo) {} 17 | 18 | if (foo) foo(); 19 | else try { doStuff(); } finally { doMoreStuff(); } 20 | 21 | if (foo) 22 | foo(); 23 | 24 | if (foo) 25 | if (bar) 26 | foo(bar); 27 | 28 | if (foo) { 29 | if (bar) 30 | foo(bar); 31 | } else 32 | bar(); 33 | 34 | if (foo) 35 | if (bar) 36 | foo(bar); 37 | else 38 | foo(); 39 | else {} 40 | 41 | if (foo) 42 | foo(); 43 | else 44 | bar(); 45 | 46 | if (foo) foo(); else {} 47 | 48 | if (foo) 49 | foo(); 50 | else 51 | bar(); 52 | 53 | if (foo) 54 | foo(); 55 | else if (bar) 56 | bar(); 57 | else 58 | baz(); 59 | -------------------------------------------------------------------------------- /test/rules/ext-curly/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | for (;;) {} 2 | for (let foo of bar) ; 3 | for (let key in obj) {foo();} 4 | ~~~~~~~~ [0] 5 | while (true) doStuff(); 6 | do { 7 | foo(); 8 | bar(); 9 | } while (true); 10 | do { 11 | ~ 12 | foo(); 13 | ~~~~~~~~~~ 14 | } while (true); 15 | ~ [0] 16 | 17 | while (true) 18 | switch (foo) {} 19 | while (false) { 20 | ~ 21 | switch (foo) {} 22 | ~~~~~~~~~~~~~~~~~~~ 23 | } 24 | ~ [0] 25 | 26 | if (foo) foo(); 27 | else try { doStuff(); } finally { doMoreStuff(); } 28 | 29 | if (foo) { 30 | ~ 31 | foo(); 32 | ~~~~~~~~~~ 33 | } 34 | ~ [0] 35 | 36 | if (foo) { 37 | ~ 38 | if (bar) { 39 | ~~~~~~~~~~~~~~ 40 | ~ 41 | foo(bar); 42 | ~~~~~~~~~~~~~~~~~ 43 | ~~~~~~~~~~~~~~~~~ 44 | } 45 | ~~~~~ 46 | ~~~~~ [0] 47 | } 48 | ~ [0] 49 | 50 | if (foo) { 51 | if (bar) { 52 | ~ 53 | foo(bar); 54 | ~~~~~~~~~~~~~~~~~ 55 | } 56 | ~~~~~ [0] 57 | } else { 58 | ~ 59 | bar(); 60 | ~~~~~~~~~~ 61 | } 62 | ~ [0] 63 | 64 | if (foo) { 65 | ~ 66 | if (bar) 67 | ~~~~~~~~~~~~ 68 | foo(bar); 69 | ~~~~~~~~~~~~~~~~~ 70 | else 71 | ~~~~~~~~ 72 | foo(); 73 | ~~~~~~~~~~~~~~ 74 | } else {} 75 | ~ [0] 76 | 77 | if (foo) 78 | foo(); 79 | else 80 | bar(); 81 | 82 | if (foo) { foo(); } else {} 83 | ~~~~~~~~~~ [0] 84 | 85 | if (foo) 86 | foo(); 87 | else { 88 | ~ 89 | bar(); 90 | ~~~~~~~~~~ 91 | } 92 | ~ [0] 93 | 94 | if (foo) { 95 | ~ 96 | foo(); 97 | ~~~~~~~~~~ 98 | } else if (bar) { 99 | ~ [0] 100 | ~ 101 | bar(); 102 | ~~~~~~~~~~ 103 | } else { 104 | ~ [0] 105 | ~ 106 | baz(); 107 | ~~~~~~~~~~ 108 | } 109 | ~ [0] 110 | 111 | [0]: unnecessary curly braces -------------------------------------------------------------------------------- /test/rules/ext-curly/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "ext-curly": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/ext-curly/else-braced-child/test.ts.lint: -------------------------------------------------------------------------------- 1 | while (true) 2 | if (foo) 3 | foo(); 4 | 5 | for (;;) 6 | if (foo) foo(); 7 | ~~~~~~~~~~~~~~~ 8 | ~~~~~~ [1] 9 | else bar(); 10 | ~~~~~~~~~~~~~~~ [1] 11 | ~~~~~~ [1] 12 | 13 | [1]: statement must be braced -------------------------------------------------------------------------------- /test/rules/ext-curly/else-braced-child/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "ext-curly": [true, "braced-child", "else"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/ext-curly/else/test.ts.lint: -------------------------------------------------------------------------------- 1 | if (foo) 2 | foo(); 3 | if (foo) {foo();} 4 | ~~~~~~~~ [0] 5 | if (foo) foo(); 6 | ~~~~~~[1] 7 | else bar(); 8 | ~~~~~~ [1] 9 | if (foo) {foo();} 10 | else if (bar) bar(); 11 | ~~~~~~ [1] 12 | if (foo) {foo();} 13 | else if (bar) bar(); 14 | ~~~~~~ [1] 15 | else baz(); 16 | ~~~~~~ [1] 17 | if (foo) foo() 18 | ~~~~~ [1] 19 | else { 20 | if (bar) bar(); 21 | } 22 | 23 | [0]: unnecessary curly braces 24 | [1]: statement must be braced -------------------------------------------------------------------------------- /test/rules/ext-curly/else/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "ext-curly": [true, "else"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/ext-curly/nested-if-else/test.ts.lint: -------------------------------------------------------------------------------- 1 | if (foo) 2 | if (bar) foo(bar); 3 | 4 | if (foo) 5 | if (bar) foo(bar); 6 | ~~~~~~~~~~~~~~~~~~ 7 | else foo(); 8 | ~~~~~~~~~~~~~~~ [1] 9 | 10 | if (foo) 11 | foo(); 12 | else if (bar) 13 | bar(); 14 | else 15 | baz(); 16 | 17 | if (foo) 18 | foo(); 19 | else { 20 | ~ 21 | if (bar) 22 | ~~~~~~~~~~~~ 23 | bar(); 24 | ~~~~~~~~~~~~~~ 25 | else 26 | ~~~~~~~~ 27 | baz(); 28 | ~~~~~~~~~~~~~~ 29 | } 30 | ~ [0] 31 | 32 | [0]: unnecessary curly braces 33 | [1]: statement must be braced -------------------------------------------------------------------------------- /test/rules/ext-curly/nested-if-else/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "ext-curly": [true, "nested-if-else"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/naming-convention/accessor/test.ts.lint: -------------------------------------------------------------------------------- 1 | class Test { 2 | public get getValue() {} 3 | ~~~~~~~~ [accessor name must have leading underscore] 4 | 5 | public set setValue() {} 6 | ~~~~~~~~ [accessor name must have leading underscore] 7 | 8 | public get _getValue() {} 9 | 10 | public set _setValue() {} 11 | } 12 | -------------------------------------------------------------------------------- /test/rules/naming-convention/accessor/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["../../../../rules"], 3 | "rules": { 4 | "naming-convention": [ 5 | true, 6 | {"type": "method", "leadingUnderscore": "allow"}, 7 | {"type": "accessor", "leadingUnderscore": "require"} 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/rules/naming-convention/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | var moduleCamel = "camel"; 2 | var ModulePascal = "pascal"; 3 | ~~~~~~~~~~~~ [variable name must be in strictCamelCase] 4 | const MODULE_CONST = "const" 5 | const another_const = "const" 6 | ~~~~~~~~~~~~~ [variable name must be in UPPER_CASE] 7 | const {foo, bar} = baz; 8 | ~~~ [variable name must be in UPPER_CASE] 9 | ~~~ [variable name must be in UPPER_CASE] 10 | const [foo, , bar] = baz; 11 | ~~~ [variable name must be in UPPER_CASE] 12 | ~~~ [variable name must be in UPPER_CASE] 13 | const _FOO = "const"; 14 | ~~~~ [variable name must be in UPPER_CASE] 15 | const FOO_ = "const"; 16 | ~~~~ [variable name must be in UPPER_CASE] 17 | const F__OO = "const"; 18 | ~~~~~ [variable name must be in UPPER_CASE] 19 | const F_O_O = "const"; 20 | var _noLeading = ""; 21 | ~~~~~~~~~~ [variable name must not have leading underscore] 22 | var trailing_ = ""; 23 | ~~~~~~~~~ [variable name must not have trailing underscore] 24 | var number = 42; 25 | 26 | let goodCamel, goodCameL, good123Camel, goodCamel123, baDCamel; 27 | ~~~~~~~~ [variable name must be in strictCamelCase] 28 | interface GoodPascal{} 29 | interface GoodPascaL{} 30 | interface GoodPascal123{} 31 | interface Good123Pascal{} 32 | interface BADPASCAL{} 33 | ~~~~~~~~~ [interface name must be in StrictPascalCase] 34 | interface BadPAscal{} 35 | ~~~~~~~~~ [interface name must be in StrictPascalCase] 36 | 37 | function foo({_Bar, Baz, _bas}) {} 38 | ~~~~ [parameter name must be in strictCamelCase] 39 | ~~~ [parameter name must be in strictCamelCase] 40 | function Foo([Foo, , Bar]) {} 41 | ~~~ [function name must be in strictCamelCase] 42 | ~~~ [parameter name must be in strictCamelCase] 43 | ~~~ [parameter name must be in strictCamelCase] 44 | function foo({bar: _bar, baz: ajaffBaz, foo: foo, bas: ajaffFoo}) {} 45 | ~~~~ [parameter name must start with ajaff] 46 | ~~~~ [parameter name must end with one of Renamed,Foo] 47 | ~~~~~~~~ [parameter name must be in strictCamelCase] 48 | ~~~~~~~~ [parameter name must end with one of Renamed,Foo] 49 | 50 | class TestClass { 51 | ~~~~~~~~~ [class name must start with one of Foo,Bar] 52 | private privCamel = 10; 53 | ~~~~~~~~~ [property name must have leading underscore] 54 | private _leadPriv = 10; 55 | private trailPriv_ = 10; 56 | ~~~~~~~~~~ [property name must not have trailing underscore] 57 | ~~~~~~~~~~ [property name must have leading underscore] 58 | private _snake_priv = 10; 59 | ~~~~~~~~~~~ [property name must be in strictCamelCase] 60 | 61 | public snake_pub = 10; 62 | ~~~~~~~~~ [property name must be in strictCamelCase] 63 | public _camelPub = 10; 64 | ~~~~~~~~~ [property name must not have leading underscore] 65 | 66 | public get camelPubAcc() {} 67 | public set camelPubAcc(Var) {} 68 | ~~~ [parameter name must be in strictCamelCase] 69 | private get snake_priv_acc() {} 70 | ~~~~~~~~~~~~~~ [accessor name must be in strictCamelCase] 71 | ~~~~~~~~~~~~~~ [accessor name must have leading underscore] 72 | 73 | readonly public static FOO = "foo"; 74 | readonly public static foo = "foo"; 75 | ~~~ [property name must be in UPPER_CASE] 76 | public static Bar() {}; 77 | public static bar() {}; 78 | ~~~ [method name must be in StrictPascalCase] 79 | constructor(public readonly _foo, protected bar) {} 80 | ~~~~ [parameterProperty name must not have leading underscore] 81 | ~~~ [parameterProperty name must have leading underscore] 82 | constructor(private readonly _FOO_Bar) {} 83 | toJSON() {} 84 | testtoJSON() {} 85 | ~~~~~~~~~~ [method name must be in strictCamelCase] 86 | toJSONtest() {} 87 | ~~~~~~~~~~ [method name must be in strictCamelCase] 88 | toJSON = "foo"; 89 | ~~~~~~ [property name must be in strictCamelCase] 90 | } 91 | 92 | class FooBar {} 93 | ~ [genericTypeParameter name did not match required regex] 94 | 95 | class Foo {} 96 | ~~~~ [genericTypeParameter name did not match required regex] 97 | 98 | class Foo {} 99 | ~~~~ [genericTypeParameter name did not match required regex] 100 | 101 | class BarFoo {} 102 | 103 | enum foo { 104 | ~~~ [enum name must be in StrictPascalCase] 105 | bar, 106 | ~~~ [enumMember name must be in StrictPascalCase] 107 | Baz, 108 | } 109 | const enum foo { 110 | ~~~ [enum name must be in UPPER_CASE] 111 | } 112 | 113 | export enum FOO_BAR { 114 | ~~~~~~~ [enum name must be in snake_case] 115 | } 116 | 117 | export enum foo_bar { 118 | } 119 | 120 | type foo = "bar"; 121 | ~~~ [typeAlias name must be in StrictPascalCase] 122 | 123 | type T = { 124 | // ignore parameters in index signatures 125 | [_KEY_: string]: string, 126 | [key_name: number]: string, 127 | } 128 | 129 | for (let {Foo} of []); 130 | ~~~ [variable name must be in strictCamelCase] 131 | 132 | for (let Foo in []); 133 | ~~~ [variable name must be in strictCamelCase] 134 | 135 | for (let I = 0;;); 136 | ~ [variable name must be in strictCamelCase] 137 | 138 | for (Foo of []); 139 | 140 | for (Foo in []); 141 | 142 | for (I = 0;;); 143 | 144 | export let foo = 'bar'; 145 | ~~~ [variable name must be in UPPER_CASE] 146 | export function foo(){} 147 | 148 | abstract class Foo { 149 | ~~~ [class name must start with Abstract] 150 | abstract get foo(); 151 | ~~~ [accessor name must end with Ajaff] 152 | abstract private bar(); 153 | ~~~ [method name must end with Ajaff] 154 | ~~~ [method name must have leading underscore] 155 | abstract get fooAjaff(); 156 | abstract private _barAjaff(); 157 | } 158 | abstract class AbstractFoo() {} 159 | 160 | // `functionVariable` is not defined in the config, so this should use the `variable` options. 161 | let fooFunc = () => undefined; 162 | let foo_func = () => undefined; 163 | ~~~~~~~~ [functionVariable name must be in strictCamelCase] 164 | let {foo: bar, baz: bazRenamed} = {}; 165 | ~~~ [variable name must end with Renamed] 166 | 167 | declare let _foo_: any; 168 | 169 | export default class {} 170 | export default function {} 171 | -------------------------------------------------------------------------------- /test/rules/naming-convention/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["../../../../rules"], 3 | "rules": { 4 | "naming-convention": [ 5 | true, 6 | {"type": "default", "leadingUnderscore": "forbid", "trailingUnderscore": "forbid"}, 7 | {"type": "variable", "format": "strictCamelCase"}, 8 | {"type": "variable", "modifiers": ["global","const"], "format": "UPPER_CASE", "leadingUnderscore": null, "trailingUnderscore": null}, 9 | {"type": "variable", "modifiers": "export", "format": "UPPER_CASE", "final": true}, 10 | {"type": "variable", "modifiers": "rename", "suffix": "Renamed"}, 11 | {"type": "parameter", "modifiers": "rename", "prefix": "ajaff", "suffix": ["Renamed", "Foo"]}, 12 | {"type": "parameter", "leadingUnderscore": "allow", "final": true}, 13 | {"type": "member", "format": "strictCamelCase"}, 14 | {"type": "member", "modifiers": "abstract", "suffix": "Ajaff"}, 15 | {"type": "member", "modifiers": "private", "leadingUnderscore": "require"}, 16 | {"type": "member", "modifiers": "protected", "leadingUnderscore": "require"}, 17 | {"type": "member", "modifiers": "static", "format": "StrictPascalCase"}, 18 | {"type": "method", "format": "strictCamelCase"}, 19 | {"type": "method", "modifiers": "static", "format": "StrictPascalCase"}, 20 | {"type": "method", "filter": "^toJSON$", "format": null}, 21 | {"type": "property", "modifiers": "const", "format": "UPPER_CASE", "final": true}, 22 | {"type": "property", "modifiers": ["static", "const"], "format": "UPPER_CASE", "final": true}, 23 | {"type": "property", "modifiers": ["private", "readonly"], "format": null, "prefix": "FOO_", "suffix": "Bar"}, 24 | {"type": "type", "format": "StrictPascalCase"}, 25 | {"type": "class", "prefix": ["Foo", "Bar"]}, 26 | {"type": "class", "modifiers": "abstract", "prefix": "Abstract"}, 27 | {"type": "genericTypeParameter", "format": null, "regex": "^[TUV]$"}, 28 | {"type": "enum", "modifiers": "const", "format": "UPPER_CASE"}, 29 | {"type": "enum", "modifiers": "export", "format": "snake_case"} 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/rules/naming-convention/filter/test.ts.lint: -------------------------------------------------------------------------------- 1 | function foo(baz) {} 2 | function foo(Baz) {} 3 | function foo(bar) {} 4 | function foo(Bar) {} 5 | ~~~ [parameter name must be in camelCase] 6 | -------------------------------------------------------------------------------- /test/rules/naming-convention/filter/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["../../../../rules"], 3 | "rules": { 4 | "naming-convention": [ 5 | true, 6 | {"type": "parameter", "format": ["camelCase"]}, 7 | {"type": "parameter", "format": ["PascalCase"], "filter": "Baz"} 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/rules/naming-convention/function-variables/test.ts.lint: -------------------------------------------------------------------------------- 1 | // Default - should be snake_case. 2 | let default_snake = ""; 3 | let DefaultPascal = ""; 4 | ~~~~~~~~~~~~~ [variable name must be in snake_case] 5 | 6 | 7 | // Exported variables - should be UPPER_CASE. 8 | export let EXPORTED_VAR = ""; 9 | export let exportedVar = ""; 10 | ~~~~~~~~~~~ [variable name must be in UPPER_CASE] 11 | 12 | 13 | // Exported functions - should be camelCase. 14 | export function funcCamel() {} 15 | export function FUNC_CAMEL() {} 16 | ~~~~~~~~~~ [function name must be in camelCase] 17 | 18 | 19 | // Exported function variables - should be in PascalCase. 20 | export let FuncArrowVar = () => undefined; 21 | export let FUNC_ARROW_VAR = () => undefined; 22 | ~~~~~~~~~~~~~~ [functionVariable name must be in PascalCase] 23 | export let FuncExpressionVar = function() {}; 24 | export let FUNC_EXPRESSION_VAR = function() {}; 25 | ~~~~~~~~~~~~~~~~~~~ [functionVariable name must be in PascalCase] 26 | 27 | 28 | // Const function variables - should be in camelCase. 29 | const funcArrowVar = () => undefined; 30 | const FUNC_ARROW_VAR = () => undefined; 31 | ~~~~~~~~~~~~~~ [functionVariable name must be in camelCase] 32 | const funcExpressionVar = function() {}; 33 | const FUNC_EXPRESSION_VAR = function() {}; 34 | ~~~~~~~~~~~~~~~~~~~ [functionVariable name must be in camelCase] 35 | 36 | 37 | // Named functions - should be in snake_case (the default). 38 | export let NamedFuncExpressionVar = function named_foo() {}; 39 | export let NAMED_FUNC_EXPRESSION_VAR = function NAMED_FOO() {}; 40 | ~~~~~~~~~~~~~~~~~~~~~~~~~ [functionVariable name must be in PascalCase] 41 | ~~~~~~~~~ [function name must be in snake_case] 42 | 43 | 44 | // Local function variables - should be UPPER_CASE. 45 | function local_test() { 46 | let LOCAL_FUNC = () => undefined; 47 | let localFunc = () => undefined; 48 | ~~~~~~~~~ [functionVariable name must be in UPPER_CASE] 49 | } 50 | -------------------------------------------------------------------------------- /test/rules/naming-convention/function-variables/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["../../../../rules"], 3 | "rules": { 4 | "naming-convention": [ 5 | true, 6 | { "type": "default", "format": "snake_case" }, 7 | { "type": "variable", "modifiers": "export", "format": "UPPER_CASE" }, 8 | { "type": "function", "modifiers": "export", "format": "camelCase" }, 9 | { "type": "functionVariable", "modifiers": "export", "format": "PascalCase" }, 10 | { "type": "functionVariable", "modifiers": "local", "format": "UPPER_CASE" }, 11 | { "type": "functionVariable", "modifiers": "const", "format": "camelCase" } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/rules/naming-convention/multi-format/test.ts.lint: -------------------------------------------------------------------------------- 1 | function foo() {} 2 | function fooBar() {} 3 | function Foo() {} 4 | ~~~ [1] 5 | function FooBar( {}) 6 | ~~~~~~ [1] 7 | function FOO() {} 8 | function FOO_BAR() {} 9 | function foo_bar() {} 10 | ~~~~~~~ [1] 11 | 12 | var foo; 13 | var Foo; 14 | var FOO; 15 | var fooBar; 16 | var FooBar; 17 | var FOO_BAR; 18 | var foo_bar; 19 | ~~~~~~~ [variable name must be in UPPER_CASE, camelCase or PascalCase] 20 | 21 | (function() { 22 | function foo() {} 23 | function fooBar() {} 24 | function Foo() {} 25 | ~~~ [0] 26 | function FooBar( {}) 27 | ~~~~~~ [0] 28 | function FOO() {} 29 | ~~~ [0] 30 | function FOO_BAR() {} 31 | ~~~~~~~ [0] 32 | function foo_bar() {} 33 | ~~~~~~~ [0] 34 | })() 35 | 36 | [0]: function name must be in camelCase 37 | [1]: function name must be in UPPER_CASE or camelCase 38 | -------------------------------------------------------------------------------- /test/rules/naming-convention/multi-format/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["../../../../rules"], 3 | "rules": { 4 | "naming-convention": [ 5 | true, 6 | {"type": "function", "format": ["UPPER_CASE", "camelCase"]}, 7 | {"type": "function", "modifiers": "local", "format": "camelCase"}, 8 | {"type": "variable", "format": ["UPPER_CASE", "camelCase", "PascalCase"]} 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/rules/naming-convention/parameter-property/test.ts.lint: -------------------------------------------------------------------------------- 1 | export class Point { 2 | constructor(protected x: number, protected y: number, Z: number, public selected: boolean) {} 3 | ~ [parameterProperty name must be in UPPER_CASE] 4 | ~ [parameter name must be in camelCase] 5 | ~~~~~~~~ [parameterProperty name must be in PascalCase] 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/naming-convention/parameter-property/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "../../../../rules" 4 | ], 5 | "rules": { 6 | "naming-convention": [ 7 | true, 8 | {"type": "default", "format": "PascalCase", "leadingUnderscore": "forbid", "trailingUnderscore": "forbid"}, 9 | {"type": "parameter", "format": "camelCase"}, 10 | {"type": "variable", "format": "camelCase"}, 11 | {"type": "property", "format": "camelCase"}, 12 | {"type": "property", "modifiers": ["public"], "format": "PascalCase"}, 13 | {"type": "parameterProperty", "modifiers": "protected", "format": "UPPER_CASE"}, 14 | {"type": "parameterProperty", "filter": "x", "format": "camelCase"} 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/rules/naming-convention/strict-formats/test.ts.lint: -------------------------------------------------------------------------------- 1 | function foo() {} 2 | function innerHTML() {} 3 | ~~~~~~~~~ [function name must be in strictCamelCase] 4 | function innerHtml() {} 5 | function Foo() {} 6 | ~~~ [function name must be in strictCamelCase] 7 | function InnerHtml() {} 8 | ~~~~~~~~~ [function name must be in strictCamelCase] 9 | function MOAR() {} 10 | ~~~~ [function name must be in strictCamelCase] 11 | function a_() {} 12 | ~~ [function name must be in strictCamelCase] 13 | function a() {} 14 | function B() {} 15 | ~ [function name must be in strictCamelCase] 16 | 17 | let foo, 18 | ~~~ [variable name must be in StrictPascalCase] 19 | innerHTML, 20 | ~~~~~~~~~ [variable name must be in StrictPascalCase] 21 | innerHtml, 22 | ~~~~~~~~~ [variable name must be in StrictPascalCase] 23 | Foo, 24 | InnerHtml, 25 | InnerHTML, 26 | ~~~~~~~~~ [variable name must be in StrictPascalCase] 27 | MOAR, 28 | ~~~~ [variable name must be in StrictPascalCase] 29 | a_, 30 | ~~ [variable name must be in StrictPascalCase] 31 | _a, 32 | ~~ [variable name must be in StrictPascalCase] 33 | A_, 34 | ~~ [variable name must be in StrictPascalCase] 35 | _A, 36 | ~~ [variable name must be in StrictPascalCase] 37 | a, 38 | ~ [variable name must be in StrictPascalCase] 39 | B; 40 | 41 | namespace ns { 42 | function foo() {} 43 | function innerHTML() {} 44 | function innerHtml() {} 45 | function Foo() {} 46 | ~~~ [function name must be in camelCase] 47 | function InnerHtml() {} 48 | ~~~~~~~~~ [function name must be in camelCase] 49 | function MOAR() {} 50 | ~~~~ [function name must be in camelCase] 51 | function a_() {} 52 | ~~ [function name must be in camelCase] 53 | function _a() {} 54 | ~~ [function name must be in camelCase] 55 | function a() {} 56 | function B() {} 57 | ~ [function name must be in camelCase] 58 | 59 | let foo, 60 | ~~~ [variable name must be in PascalCase] 61 | innerHTML, 62 | ~~~~~~~~~ [variable name must be in PascalCase] 63 | innerHtml, 64 | ~~~~~~~~~ [variable name must be in PascalCase] 65 | Foo, 66 | InnerHtml, 67 | InnerHTML, 68 | MOAR, 69 | a_, 70 | ~~ [variable name must be in PascalCase] 71 | _a, 72 | ~~ [variable name must be in PascalCase] 73 | A_, 74 | ~~ [variable name must be in PascalCase] 75 | _A, 76 | ~~ [variable name must be in PascalCase] 77 | a, 78 | ~ [variable name must be in PascalCase] 79 | B; 80 | } 81 | -------------------------------------------------------------------------------- /test/rules/naming-convention/strict-formats/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["../../../../rules"], 3 | "rules": { 4 | "naming-convention": [ 5 | true, 6 | {"type": "function", "format": "strictCamelCase"}, 7 | {"type": "function", "modifiers": "local", "format": ["camelCase"]}, 8 | {"type": "variable", "format": "StrictPascalCase"}, 9 | {"type": "variable", "modifiers": "local", "format": "PascalCase"} 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/rules/naming-convention/this/test.ts.lint: -------------------------------------------------------------------------------- 1 | function foo(this: any, that, bar) {} 2 | ~~~~ [parameter name must have leading underscore] 3 | ~~~ [parameter name must have leading underscore] 4 | function foo(_this: any) {} 5 | -------------------------------------------------------------------------------- /test/rules/naming-convention/this/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["../../../../rules"], 3 | "rules": { 4 | "naming-convention": [ 5 | true, 6 | {"type": "parameter", "leadingUnderscore": "require"} 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/rules/naming-convention/unused/test.ts.lint: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | function _unused(_param): void; 4 | ~~~~~~~ [err % ('function')] 5 | ~~~~~~ [err % ('parameter')] 6 | function _unused({_foo}): void; 7 | ~~~~~~~ [err % ('function')] 8 | ~~~~ [err % ('parameter')] 9 | function _unused(): void; 10 | ~~~~~~~ [err % ('function')] 11 | function _unused(_param) {} 12 | ~~~~~~~ [err % ('function')] 13 | 14 | function foo(_param, {_p1, _p2}) { 15 | ~~~~~~ [err % ('parameter')] 16 | ~~~ [err % ('parameter')] 17 | _param = _param; 18 | _p1; 19 | } 20 | 21 | class _Foo<_T> { 22 | ~~ [err % ('genericTypeParameter')] 23 | ~~~~ [err % ('class')] 24 | constructor(_foo, private _bar, public _bar, _bas) { _bas; } 25 | ~~~~ [err % ('parameterProperty')] 26 | ~~~~ [err % ('parameterProperty')] 27 | ~~~~ [err % ('parameter')] 28 | } 29 | 30 | let _foo; 31 | ~~~~ [err % ('variable')] 32 | _foo; 33 | 34 | [err]: %s name must not have leading underscore 35 | -------------------------------------------------------------------------------- /test/rules/naming-convention/unused/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["../../../../rules"], 3 | "rules": { 4 | "naming-convention": [ 5 | true, 6 | {"type": "default", "leadingUnderscore": "forbid"}, 7 | {"type": "default", "modifiers": "unused", "leadingUnderscore": "allow"} 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/rules/no-accessor-recursion/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | let obj = { 2 | get foo() { 3 | return this._foo; 4 | }, 5 | 6 | set foo(v: string) { 7 | this._foo = v; 8 | }, 9 | 10 | get bar() { 11 | return this.bar; 12 | ~~~~~~~~ [fail] 13 | }, 14 | 15 | set bar(v: string) { 16 | this.bar = v; 17 | ~~~~~~~~ [fail] 18 | }, 19 | 20 | get baz() { 21 | return that.baz; 22 | }, 23 | 24 | set baz(v: string) { 25 | that.baz = v; 26 | }, 27 | 28 | get bas() { 29 | return (() => this.bas)(); 30 | ~~~~~~~~ [fail] 31 | }, 32 | 33 | set bas(v: string) { 34 | (() => this.bas = v)(); 35 | ~~~~~~~~ [fail] 36 | }, 37 | fn() { 38 | this.foo = this.foo; 39 | this.bar = this.bar; 40 | this.baz = this.baz; 41 | this.bas = this.bas; 42 | } 43 | } 44 | 45 | obj = { 46 | get foo() { 47 | return { 48 | get bar() { 49 | class Foo { 50 | get foo() { 51 | return this.foo; 52 | ~~~~~~~~ [fail] 53 | } 54 | get bar() { 55 | return this.bar; 56 | ~~~~~~~~ [fail] 57 | } 58 | getBaz() { 59 | return this.foo + this.bar; 60 | } 61 | } 62 | return this.foo; 63 | } 64 | } 65 | }, 66 | get bar() { 67 | this.bar++; 68 | ~~~~~~~~ [fail] 69 | return 1; 70 | }, 71 | set bar(v: number) { 72 | const tmp = this.bar; 73 | ~~~~~~~~ [fail] 74 | doStuff(v + tmp); 75 | }, 76 | get baz() { 77 | if (condition) { 78 | return this.baz; 79 | ~~~~~~~~ [fail] 80 | } 81 | return this.bar; 82 | } 83 | } 84 | 85 | abstract class MyClass { 86 | abstract get prop(): string; 87 | 88 | set prop(v: string) { 89 | this.prop = v; 90 | ~~~~~~~~~ [fail] 91 | } 92 | } 93 | 94 | [fail]: accessor recursion is not allowed 95 | -------------------------------------------------------------------------------- /test/rules/no-accessor-recursion/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-accessor-recursion": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-as-type-assertion/default/ordering.ts.fix: -------------------------------------------------------------------------------- 1 | foo = bar; 2 | foo = bar; 3 | 4 | -------------------------------------------------------------------------------- /test/rules/no-as-type-assertion/default/ordering.ts.lint: -------------------------------------------------------------------------------- 1 | foo = bar as boolean as number; 2 | ~~~~~~~~~~~~~~~~~~~~ [fail] 3 | foo = bar as number as boolean; 4 | ~~~~~~~~~~~~~~~~~~~~ [fail] 5 | 6 | [fail]: use instead of `as Type` 7 | -------------------------------------------------------------------------------- /test/rules/no-as-type-assertion/default/test.ts.fix: -------------------------------------------------------------------------------- 1 | foo = bar; 2 | 3 | foo = (bar); 4 | 5 | foo = bar; 6 | 7 | foo = bar; 8 | 9 | foo = <{foo: number}>bar; 10 | 11 | foo = bar; 12 | 13 | foo = bar; 14 | 15 | foo = bar; 16 | 17 | foo = bar; 18 | 19 | foo = bar; 20 | foo = bar; 21 | foo = bar; 22 | foo = bar; 23 | 24 | -------------------------------------------------------------------------------- /test/rules/no-as-type-assertion/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | foo = bar as number; 2 | ~~~~~~~~~ [fail] 3 | 4 | foo = (bar) as number; 5 | ~~~~~~~~~ [fail] 6 | 7 | foo = bar as number; 8 | ~~~~~~~~~ [fail] 9 | 10 | foo = bar as string; 11 | ~~~~~~~~~ [fail] 12 | 13 | foo = bar as {foo: number}; 14 | ~~~~~~~~~~~~~~~~ [fail] 15 | 16 | foo = bar as number|undefined; 17 | ~~~~~~~~~~~~~~~~~~~ [fail] 18 | 19 | foo = bar as any as number; 20 | ~~~~~~~~~~~~~~~~ [fail] 21 | 22 | foo = bar as string as any; 23 | ~~~~~~~~~~~~~~~~ [fail] 24 | 25 | foo = bar as string as any; 26 | ~~~~~~~~~~~~~~~~ [fail] 27 | 28 | foo = bar as A as any; 29 | ~~~~~~~~~~~ [fail] 30 | foo = bar as any as A; 31 | ~~~~~~~~~~~ [fail] 32 | foo = bar as AsyncIterableIterator as any; 33 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [fail] 34 | foo = bar as any as AsyncIterableIterator; 35 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [fail] 36 | 37 | [fail]: use instead of `as Type` 38 | -------------------------------------------------------------------------------- /test/rules/no-as-type-assertion/default/test.tsx.lint: -------------------------------------------------------------------------------- 1 | foo = bar as number; // it's OK in tsx and jsx files 2 | -------------------------------------------------------------------------------- /test/rules/no-as-type-assertion/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-as-type-assertion": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-collapsible-if/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | if (foo) { 2 | ~~~~~~~~~~ 3 | if (bar) { 4 | ~~~~~~~~~~~~ [if] 5 | } 6 | } 7 | 8 | if (foo) 9 | ~~~~~~~~ 10 | if (bar) { 11 | ~~~~~~~~~~~~ [if] 12 | } 13 | 14 | if (foo) { 15 | if (bar) { 16 | } else {} 17 | } 18 | 19 | if (foo) { 20 | if (bar) { 21 | } 22 | } else {} 23 | 24 | if (foo) { 25 | } else { 26 | ~~~~~~ 27 | if (bar) {} 28 | ~~~~~~~~~~~~ [else] 29 | } 30 | 31 | if (foo) { 32 | } else { 33 | ~~~~~~ 34 | if (bar) { 35 | ~~~~~~~~~~~~ [else] 36 | } else {} 37 | } 38 | 39 | if (foo) { 40 | } else if (bar) { 41 | } 42 | 43 | if (foo) { 44 | } else if (bar) { 45 | } else { 46 | } 47 | 48 | [if]: if statements can be merged 49 | [else]: if statement can be merged with previous else 50 | -------------------------------------------------------------------------------- /test/rules/no-collapsible-if/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-collapsible-if": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-else-after-return/allow-else-if/test.ts.lint: -------------------------------------------------------------------------------- 1 | if (condition) { 2 | return; 3 | } else { 4 | ~~~~ [fail] 5 | return; 6 | } 7 | 8 | if (condition) { 9 | return; 10 | } else if (someOtherCondition) { 11 | return; 12 | } 13 | 14 | if (condition) { 15 | return; 16 | } else if (someOtherCondition) { 17 | return; 18 | } else { 19 | ~~~~ [fail] 20 | return; 21 | } 22 | 23 | if (condition) { 24 | return; 25 | } else if (someOtherCondition) { 26 | return; 27 | } else if (yetAnotherCondition) { 28 | return; 29 | } else { 30 | ~~~~ [fail] 31 | return; 32 | } 33 | 34 | if (condition) { 35 | // nothing 36 | } else if (someOtherCondition) { 37 | return; 38 | } else { 39 | return; 40 | } 41 | 42 | if (condition) { 43 | // nothing 44 | } else if (someOtherCondition) { 45 | return; 46 | } else if (yetAnotherCondition) { 47 | return; 48 | } else { 49 | return; 50 | } 51 | 52 | if (condition) { 53 | return; 54 | } else if (someOtherCondition) { 55 | // nothing 56 | } else if (yetAnotherCondition) { 57 | return; 58 | } else { 59 | return; 60 | } 61 | 62 | if (condition) { 63 | return; 64 | } else if (someOtherCondition) { 65 | return; 66 | } else if (yetAnotherCondition) { 67 | // nothing 68 | } else { 69 | return; 70 | } 71 | 72 | [fail]: unnecessary else after return 73 | -------------------------------------------------------------------------------- /test/rules/no-else-after-return/allow-else-if/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-else-after-return": [true, "allow-else-if"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-else-after-return/default/fix.ts.fix: -------------------------------------------------------------------------------- 1 | if (condition) { 2 | return; 3 | } 4 | -------------------------------------------------------------------------------- /test/rules/no-else-after-return/default/fix.ts.lint: -------------------------------------------------------------------------------- 1 | if (condition) { 2 | return; 3 | } else {} 4 | ~~~~ [unnecessary else after return] 5 | -------------------------------------------------------------------------------- /test/rules/no-else-after-return/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | if (condition) { 2 | return; 3 | } else {} 4 | ~~~~ [fail] 5 | 6 | if (condition) { 7 | while (true) 8 | return; 9 | } else {} 10 | ~~~~ [fail] 11 | 12 | if (condition) { 13 | while (false) 14 | return; 15 | } else {} 16 | 17 | if (condition) { 18 | while (condition) 19 | return; 20 | } else {} 21 | 22 | if (condition) return; 23 | else ; 24 | ~~~~ [fail] 25 | 26 | if (condition) { 27 | { 28 | return; 29 | } 30 | } else {} 31 | ~~~~ [fail] 32 | 33 | if (condition) { 34 | if (confition) { 35 | return; 36 | } else { 37 | ~~~~ [fail] 38 | return; 39 | } 40 | } else {} 41 | ~~~~ [fail] 42 | 43 | if (condition) { 44 | if (condition) { 45 | return; 46 | } 47 | } else {} 48 | 49 | if (condition) return; 50 | 51 | if (condition) foo(); 52 | else bar(); 53 | 54 | if (condition) { 55 | return; 56 | } 57 | 58 | if (condition) { 59 | foo(); 60 | } else { 61 | bar(); 62 | } 63 | 64 | if (condition) { 65 | { 66 | } 67 | } else {} 68 | 69 | if (condition) { 70 | if (condition) { 71 | return; 72 | } else { 73 | ~~~~ [fail] 74 | } 75 | } else {} 76 | 77 | if (condition) { 78 | if (condition) { 79 | } 80 | } else {} 81 | 82 | if (condition) { 83 | switch (foo) { 84 | case 'a': 85 | return; 86 | case 'b': 87 | return; 88 | default: 89 | return; 90 | } 91 | } else {} 92 | ~~~~ [fail] 93 | 94 | if (condition) { 95 | switch (foo) { 96 | default: 97 | return; 98 | case 'a': 99 | return; 100 | } 101 | } else {} 102 | ~~~~ [fail] 103 | 104 | if (condition) { 105 | switch (foo) { 106 | case 'a': 107 | return; 108 | case 'b': 109 | return; 110 | } 111 | } else {} 112 | 113 | if (condition) { 114 | switch (foo) { 115 | case 'a': 116 | return; 117 | case 'b': 118 | default: 119 | return; 120 | } 121 | } else {} 122 | ~~~~ [fail] 123 | 124 | if (condition) { 125 | switch (foo) { 126 | case 'a': 127 | return; 128 | case 'b': 129 | console.log(foo); 130 | // fallthrough 131 | default: 132 | return; 133 | } 134 | } else {} 135 | ~~~~ [fail] 136 | 137 | if (condition) { 138 | switch (foo) { 139 | case 'a': 140 | return; 141 | case 'b': 142 | return; 143 | default: 144 | } 145 | } else {} 146 | 147 | if (condition) { 148 | }else if (condition) { 149 | return; 150 | } else {} 151 | 152 | if (condition) { 153 | }else{ 154 | if (condition) { 155 | return; 156 | } else {} 157 | ~~~~ [fail] 158 | } 159 | 160 | if (condition) { 161 | throw null; 162 | } else {} 163 | 164 | if (condition) { 165 | if (condition) 166 | return; 167 | else 168 | ~~~~ [fail] 169 | return; 170 | } else {} 171 | ~~~~ [fail] 172 | 173 | switch (Boolean()) { 174 | case true: 175 | if (condition) { 176 | if (condition) 177 | break; 178 | return; 179 | } else {} 180 | break; 181 | case false: 182 | if (condition) { 183 | if (condition) 184 | return; 185 | return; 186 | } else {} 187 | ~~~~ [fail] 188 | } 189 | 190 | outer: for (;;) { 191 | if (condition) { 192 | switch (Boolean()) { 193 | case true: 194 | break; 195 | } 196 | return; 197 | } else {} 198 | ~~~~ [fail] 199 | 200 | if (condition) { 201 | switch (Boolean()) { 202 | case true: 203 | break outer; 204 | } 205 | return; 206 | } else {} 207 | } 208 | 209 | 210 | [fail]: unnecessary else after return 211 | -------------------------------------------------------------------------------- /test/rules/no-else-after-return/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-else-after-return": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-return-undefined/allow-void-expression/test.ts.lint: -------------------------------------------------------------------------------- 1 | function foo(bar) { 2 | return; 3 | } 4 | 5 | function foo(bar) { 6 | return bar; 7 | } 8 | 9 | function foo(bar) { 10 | return null; 11 | } 12 | 13 | function foo(bar) { 14 | return undefined; 15 | ~~~~~~~~~ [fail] 16 | } 17 | 18 | function foo(bar) { 19 | return void 0; 20 | ~~~~~~ [fail] 21 | } 22 | 23 | function foo(bar) { 24 | return void bar; 25 | } 26 | 27 | [fail]: don't return explicit undefined -------------------------------------------------------------------------------- /test/rules/no-return-undefined/allow-void-expression/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-return-undefined": [ 5 | true, 6 | "allow-void-expression" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/rules/no-return-undefined/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | function foo(bar) { 2 | return; 3 | } 4 | 5 | function foo(bar) { 6 | return bar; 7 | } 8 | 9 | function foo(bar) { 10 | return null; 11 | } 12 | 13 | function foo(bar) { 14 | return undefined; 15 | ~~~~~~~~~ [fail] 16 | } 17 | 18 | function foo(bar) { 19 | return void 0; 20 | ~~~~~~ [fail] 21 | } 22 | 23 | function foo(bar) { 24 | return void bar; 25 | ~~~~~~~~ [fail] 26 | } 27 | 28 | [fail]: don't return explicit undefined -------------------------------------------------------------------------------- /test/rules/no-return-undefined/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-return-undefined": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-static-this/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | class Foo { 2 | static bar() { 3 | this.foo = this.bar; 4 | ~~~~ [fail] 5 | ~~~~ [fail] 6 | return this; 7 | ~~~~ [fail] 8 | } 9 | 10 | static get baz() { 11 | return this.baz; 12 | ~~~~ [fail] 13 | } 14 | 15 | static set bas(v) { 16 | this.bas = v; 17 | ~~~~ [fail] 18 | } 19 | 20 | static ajaff() { 21 | function moar() { 22 | return this; 23 | } 24 | return () => this; 25 | ~~~~ [fail] 26 | } 27 | } 28 | 29 | [fail]: don't use this in static methods 30 | -------------------------------------------------------------------------------- /test/rules/no-static-this/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-static-this": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-unnecessary-else/default/test.ts.fix: -------------------------------------------------------------------------------- 1 | if (condition) { 2 | return; 3 | } 4 | 5 | if (condition) { 6 | return; 7 | } { 8 | const foo = "bar"; 9 | } 10 | 11 | if (condition) { 12 | return; 13 | } 14 | var foo = "bar"; 15 | 16 | 17 | if (condition) { 18 | return; 19 | } { 20 | class C {} 21 | } 22 | 23 | if (condition) { 24 | return; 25 | } 26 | { const foo = "bar"; } 27 | return foo; 28 | 29 | 30 | if (condition) return; 31 | ; 32 | 33 | if (condition) { 34 | { 35 | return; 36 | } 37 | } 38 | 39 | if (condition) { 40 | if (condition) { 41 | return; 42 | } 43 | return; 44 | 45 | } 46 | 47 | if (condition) { 48 | if (condition) { 49 | return; 50 | } 51 | } else {} 52 | 53 | if (condition) return; 54 | 55 | if (condition) foo(); 56 | else bar(); 57 | 58 | if (condition) { 59 | return; 60 | } 61 | 62 | if (condition) { 63 | foo(); 64 | } else { 65 | bar(); 66 | } 67 | 68 | if (condition) { 69 | { 70 | } 71 | } else {} 72 | 73 | if (condition) { 74 | if (condition) { 75 | return; 76 | } 77 | 78 | } else {} 79 | 80 | if (condition) { 81 | if (condition) { 82 | } 83 | } else {} 84 | 85 | if (condition) { 86 | switch (foo) { 87 | case 'a': 88 | return; 89 | case 'b': 90 | return; 91 | default: 92 | return; 93 | } 94 | } 95 | 96 | if (condition) { 97 | switch (foo) { 98 | default: 99 | return; 100 | case 'a': 101 | return; 102 | } 103 | } 104 | 105 | if (condition) { 106 | switch (foo) { 107 | case 'a': 108 | return; 109 | case 'b': 110 | return; 111 | } 112 | } else {} 113 | 114 | for (;;) { 115 | if (condition) { 116 | continue; 117 | } 118 | 119 | if (condition) { 120 | break; 121 | } 122 | 123 | switch (foo) { 124 | case 1: 125 | if (foo) { 126 | continue; 127 | } 128 | 129 | if (foo) { 130 | break; 131 | } 132 | } 133 | 134 | if (condition) { 135 | switch (foo) { 136 | case 1: 137 | continue; 138 | default: 139 | console.log('foo'); 140 | // fallthrough 141 | case 2: 142 | return; 143 | } 144 | } 145 | 146 | if (condition) { 147 | switch (foo) { 148 | case 1: 149 | break; 150 | default: 151 | case 2: 152 | return; 153 | } 154 | } else {} 155 | } 156 | 157 | if (condition) { 158 | throw 'foo'; 159 | } 160 | 161 | if (condition) 162 | throw 'foo'; 163 | ; 164 | 165 | if (condition) { 166 | }else if (condition) { 167 | return; 168 | } else {} 169 | 170 | if (condition) { 171 | }else{ 172 | if (condition) { 173 | return; 174 | } 175 | } 176 | 177 | if (condition) { 178 | try { 179 | } catch { 180 | return; 181 | } 182 | } else {} 183 | 184 | if (condition) { 185 | try { 186 | return; 187 | } catch { 188 | } 189 | } else {} 190 | 191 | if (condition) { 192 | try { 193 | } catch { 194 | } finally { 195 | return; 196 | } 197 | } 198 | 199 | if (condition) { 200 | try { 201 | return; 202 | } catch { 203 | } finally { 204 | } 205 | } else {} 206 | 207 | if (condition) { 208 | try { 209 | } finally { 210 | return; 211 | } 212 | } 213 | 214 | if (condition) { 215 | try { 216 | return; 217 | } finally { 218 | } 219 | } 220 | 221 | if (condition) { 222 | for (;;) { 223 | try { 224 | return; 225 | } catch { 226 | return; 227 | } finally { 228 | break; 229 | } 230 | } 231 | } else {} 232 | 233 | if (condition) { 234 | for (;;) { 235 | try { 236 | return; 237 | } finally { 238 | break; 239 | } 240 | } 241 | } else {} 242 | 243 | if (condition) { 244 | for (;;) { 245 | try { 246 | break; 247 | } finally { 248 | return; 249 | } 250 | } 251 | } 252 | 253 | if (condition) { 254 | for (;;) { 255 | try { 256 | break; 257 | } finally { 258 | continue; 259 | } 260 | } 261 | } else {} 262 | 263 | if (condition) { 264 | foo: for (;;) { 265 | if (condition) { 266 | continue; 267 | } 268 | break; 269 | 270 | } 271 | } else {} 272 | 273 | if (condition) { 274 | foo: for (;;) { 275 | if (condition) 276 | while (true) { 277 | if (condition) { 278 | continue foo; 279 | } 280 | break foo; 281 | 282 | } 283 | 284 | } 285 | } else {} 286 | 287 | if (condition) { 288 | foo: for (;;) { 289 | if (condition) 290 | while (condition) { 291 | if (condition) 292 | continue; 293 | if (condition) { 294 | continue foo; 295 | } 296 | break foo; 297 | 298 | } 299 | else {} 300 | } 301 | } else {} 302 | 303 | foo: for (;;) { 304 | if (condition) 305 | switch (v) { 306 | case 1: 307 | if (condition) { 308 | break; 309 | } 310 | default: 311 | continue foo; 312 | } 313 | else {} 314 | } 315 | 316 | foo: for (;;) 317 | if (condition) 318 | for (;;) 319 | try { 320 | break foo; 321 | } catch { 322 | continue foo; 323 | } finally { 324 | break; 325 | } 326 | else {} 327 | 328 | if (condition) { 329 | try { 330 | throw 'foo'; 331 | } catch {} 332 | } else {} 333 | 334 | if (condition) { 335 | try { 336 | 'statement'; 337 | } catch { 338 | throw 'foo'; 339 | } 340 | } else {} 341 | 342 | if (condition) { 343 | try { 344 | return foo(); 345 | } catch { 346 | throw 'foo'; 347 | } 348 | } 349 | 350 | if (condition) { 351 | try { 352 | return foo(); 353 | } finally { 354 | } 355 | } 356 | 357 | if (condition) { 358 | try { 359 | } finally { 360 | return; 361 | } 362 | } 363 | 364 | foo: bar: for (;;) { 365 | if (condition) 366 | for (;;) 367 | continue foo; 368 | 369 | 370 | if (condition) 371 | for (;;) 372 | break bar; 373 | 374 | 375 | if (condition) 376 | baz: bas: for (;;) 377 | break bas; 378 | else {} 379 | 380 | if (condition) 381 | baz: bas: for (;;) 382 | break baz; 383 | else {} 384 | } 385 | 386 | -------------------------------------------------------------------------------- /test/rules/no-unnecessary-else/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | if (condition) { 2 | return; 3 | } else {} 4 | ~~~~ [fail] 5 | 6 | if (condition) { 7 | return; 8 | } else { 9 | ~~~~ [fail] 10 | const foo = "bar"; 11 | } 12 | 13 | if (condition) { 14 | return; 15 | } else { 16 | ~~~~ [fail] 17 | var foo = "bar"; 18 | } 19 | 20 | if (condition) { 21 | return; 22 | } else { 23 | ~~~~ [fail] 24 | class C {} 25 | } 26 | 27 | if (condition) { 28 | return; 29 | } else { 30 | ~~~~ [fail] 31 | { const foo = "bar"; } 32 | return foo; 33 | } 34 | 35 | if (condition) return; 36 | else ; 37 | ~~~~ [fail] 38 | 39 | if (condition) { 40 | { 41 | return; 42 | } 43 | } else {} 44 | ~~~~ [fail] 45 | 46 | if (condition) { 47 | if (condition) { 48 | return; 49 | } else { 50 | ~~~~ [fail] 51 | return; 52 | } 53 | } else {} 54 | ~~~~ [fail] 55 | 56 | if (condition) { 57 | if (condition) { 58 | return; 59 | } 60 | } else {} 61 | 62 | if (condition) return; 63 | 64 | if (condition) foo(); 65 | else bar(); 66 | 67 | if (condition) { 68 | return; 69 | } 70 | 71 | if (condition) { 72 | foo(); 73 | } else { 74 | bar(); 75 | } 76 | 77 | if (condition) { 78 | { 79 | } 80 | } else {} 81 | 82 | if (condition) { 83 | if (condition) { 84 | return; 85 | } else { 86 | ~~~~ [fail] 87 | } 88 | } else {} 89 | 90 | if (condition) { 91 | if (condition) { 92 | } 93 | } else {} 94 | 95 | if (condition) { 96 | switch (foo) { 97 | case 'a': 98 | return; 99 | case 'b': 100 | return; 101 | default: 102 | return; 103 | } 104 | } else {} 105 | ~~~~ [fail] 106 | 107 | if (condition) { 108 | switch (foo) { 109 | default: 110 | return; 111 | case 'a': 112 | return; 113 | } 114 | } else {} 115 | ~~~~ [fail] 116 | 117 | if (condition) { 118 | switch (foo) { 119 | case 'a': 120 | return; 121 | case 'b': 122 | return; 123 | } 124 | } else {} 125 | 126 | for (;;) { 127 | if (condition) { 128 | continue; 129 | } else {} 130 | ~~~~ [fail] 131 | 132 | if (condition) { 133 | break; 134 | } else {} 135 | ~~~~ [fail] 136 | 137 | switch (foo) { 138 | case 1: 139 | if (foo) { 140 | continue; 141 | } else {} 142 | ~~~~ [fail] 143 | 144 | if (foo) { 145 | break; 146 | } else {} 147 | ~~~~ [fail] 148 | } 149 | 150 | if (condition) { 151 | switch (foo) { 152 | case 1: 153 | continue; 154 | default: 155 | console.log('foo'); 156 | // fallthrough 157 | case 2: 158 | return; 159 | } 160 | } else {} 161 | ~~~~ [fail] 162 | 163 | if (condition) { 164 | switch (foo) { 165 | case 1: 166 | break; 167 | default: 168 | case 2: 169 | return; 170 | } 171 | } else {} 172 | } 173 | 174 | if (condition) { 175 | throw 'foo'; 176 | } else {} 177 | ~~~~ [fail] 178 | 179 | if (condition) 180 | throw 'foo'; 181 | else; 182 | ~~~~ [fail] 183 | 184 | if (condition) { 185 | }else if (condition) { 186 | return; 187 | } else {} 188 | 189 | if (condition) { 190 | }else{ 191 | if (condition) { 192 | return; 193 | } else {} 194 | ~~~~ [fail] 195 | } 196 | 197 | if (condition) { 198 | try { 199 | } catch { 200 | return; 201 | } 202 | } else {} 203 | 204 | if (condition) { 205 | try { 206 | return; 207 | } catch { 208 | } 209 | } else {} 210 | 211 | if (condition) { 212 | try { 213 | } catch { 214 | } finally { 215 | return; 216 | } 217 | } else {} 218 | ~~~~ [fail] 219 | 220 | if (condition) { 221 | try { 222 | return; 223 | } catch { 224 | } finally { 225 | } 226 | } else {} 227 | 228 | if (condition) { 229 | try { 230 | } finally { 231 | return; 232 | } 233 | } else {} 234 | ~~~~ [fail] 235 | 236 | if (condition) { 237 | try { 238 | return; 239 | } finally { 240 | } 241 | } else {} 242 | ~~~~ [fail] 243 | 244 | if (condition) { 245 | for (;;) { 246 | try { 247 | return; 248 | } catch { 249 | return; 250 | } finally { 251 | break; 252 | } 253 | } 254 | } else {} 255 | 256 | if (condition) { 257 | for (;;) { 258 | try { 259 | return; 260 | } finally { 261 | break; 262 | } 263 | } 264 | } else {} 265 | 266 | if (condition) { 267 | for (;;) { 268 | try { 269 | break; 270 | } finally { 271 | return; 272 | } 273 | } 274 | } else {} 275 | ~~~~ [fail] 276 | 277 | if (condition) { 278 | for (;;) { 279 | try { 280 | break; 281 | } finally { 282 | continue; 283 | } 284 | } 285 | } else {} 286 | 287 | if (condition) { 288 | foo: for (;;) { 289 | if (condition) { 290 | continue; 291 | } else { 292 | ~~~~ [fail] 293 | break; 294 | } 295 | } 296 | } else {} 297 | 298 | if (condition) { 299 | foo: for (;;) { 300 | if (condition) 301 | while (true) { 302 | if (condition) { 303 | continue foo; 304 | } else { 305 | ~~~~ [fail] 306 | break foo; 307 | } 308 | } 309 | else {} 310 | ~~~~ [fail] 311 | } 312 | } else {} 313 | 314 | if (condition) { 315 | foo: for (;;) { 316 | if (condition) 317 | while (condition) { 318 | if (condition) 319 | continue; 320 | if (condition) { 321 | continue foo; 322 | } else { 323 | ~~~~ [fail] 324 | break foo; 325 | } 326 | } 327 | else {} 328 | } 329 | } else {} 330 | 331 | foo: for (;;) { 332 | if (condition) 333 | switch (v) { 334 | case 1: 335 | if (condition) { 336 | break; 337 | } else {} 338 | ~~~~ [fail] 339 | default: 340 | continue foo; 341 | } 342 | else {} 343 | } 344 | 345 | foo: for (;;) 346 | if (condition) 347 | for (;;) 348 | try { 349 | break foo; 350 | } catch { 351 | continue foo; 352 | } finally { 353 | break; 354 | } 355 | else {} 356 | 357 | if (condition) { 358 | try { 359 | throw 'foo'; 360 | } catch {} 361 | } else {} 362 | 363 | if (condition) { 364 | try { 365 | 'statement'; 366 | } catch { 367 | throw 'foo'; 368 | } 369 | } else {} 370 | 371 | if (condition) { 372 | try { 373 | return foo(); 374 | } catch { 375 | throw 'foo'; 376 | } 377 | } else {} 378 | ~~~~ [fail] 379 | 380 | if (condition) { 381 | try { 382 | return foo(); 383 | } finally { 384 | } 385 | } else {} 386 | ~~~~ [fail] 387 | 388 | if (condition) { 389 | try { 390 | } finally { 391 | return; 392 | } 393 | } else {} 394 | ~~~~ [fail] 395 | 396 | foo: bar: for (;;) { 397 | if (condition) 398 | for (;;) 399 | continue foo; 400 | else {} 401 | ~~~~ [fail] 402 | 403 | if (condition) 404 | for (;;) 405 | break bar; 406 | else {} 407 | ~~~~ [fail] 408 | 409 | if (condition) 410 | baz: bas: for (;;) 411 | break bas; 412 | else {} 413 | 414 | if (condition) 415 | baz: bas: for (;;) 416 | break baz; 417 | else {} 418 | } 419 | 420 | [fail]: unnecessary else 421 | -------------------------------------------------------------------------------- /test/rules/no-unnecessary-else/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-unnecessary-else": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-unnecessary-type-annotation/check-return-type/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strictNullChecks": true, 4 | "target": "esnext" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-unnecessary-type-annotation/check-return-type/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-unnecessary-type-annotation": [true, "check-return-type"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-unnecessary-type-annotation/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | let foo1: number = 1; 4 | ~~~~~~~~ [fail] 5 | let foo2: 1 = 1; 6 | const foo3: number = 1; 7 | ~~~~~~~~ [fail] 8 | const foo4: 1 = 1; 9 | ~~~ [fail] 10 | const foo5: string[] = []; 11 | const foo6: string[] = ["foo", "bar"]; 12 | ~~~~~~~~~~ [fail] 13 | const foo7 = "foo"; 14 | let foo8 = "foo"; 15 | 16 | interface Foo { 17 | foo: number; 18 | } 19 | 20 | const foo9: Foo = {foo: 1}; 21 | const foo10: Foo[] = [foo9, foo9]; 22 | ~~~~~~~ [fail] 23 | 24 | const fn = function(a: string, b: number) {}; 25 | 26 | declare function take(cb: T): void; 27 | declare function generic(cb: (param: T, i: number) => T): void; 28 | declare function genericReturn(cb: () => T): T; 29 | 30 | take<() => void>((a?: boolean) => a); 31 | take<(a: string, b: number) => void>((a: string, b: number) => a); 32 | ~~~~~~~~ [fail] 33 | ~~~~~~~~ [fail] 34 | generic((a: string) => a); 35 | generic((a: string, i: number) => a); 36 | ~~~~~~~~ [fail] 37 | take<((() => void) | ((a: string) => void))>((a: string) => a); 38 | ~~~~~~~~ [fail] 39 | genericReturn((): number => { 40 | return 1; 41 | }); 42 | take<(...args: any[]) => void>(((a: any, b: any): void => {})); 43 | ~~~~~ [fail] 44 | ~~~~~ [fail] 45 | take<((a: string) => void) | ((a: string) => void)>((a: string): void => a); 46 | ~~~~~~~~ [fail] 47 | take<((a: string) => void) | ((a: string) => void) & {name?: string}>((a: string) => a); 48 | ~~~~~~~~ [fail] 49 | take<((a: string) => void) | object & {name?: string}>((a: string) => a); 50 | ~~~~~~~~ [fail] 51 | take<(((a: string) => void) | ((a: string) => void)) & {name?: string}>((a: string) => a); 52 | ~~~~~~~~ [fail] 53 | // only return type differs - don't complain about the return annotation 54 | take<((a: string) => string) | ((a: string) => number)>((a: string): string => a); 55 | ~~~~~~~~ [fail] 56 | take<((a: string) => string) | ((a: string) => number)>((a: string): string => a); 57 | ~~~~~~~~ [fail] 58 | take<( void>(a: string) => string) | ( void>(a: string) => number)>((a: string): string => a); 59 | ~~~~~~~~ [fail] 60 | 61 | (function(foo: "foo") {})("foo"); 62 | (function(foo: string) {})("foo"); 63 | ~~~~~~~~ [fail] 64 | (function(foo: string) {}("foo")); 65 | ~~~~~~~~ [fail] 66 | foo8 = function(foo: string)(foo8); 67 | ~~~~~~~~ [fail] 68 | (function(foo): void {})("foo"); 69 | (function(foo?): void {})("foo"); 70 | (function(param: string, ...foo: string[]) {})("foo", "bar", "baz"); 71 | ~~~~~~~~ [fail] 72 | ~~~~~~~~~~ [fail] 73 | (function(foo?: string) {})(); 74 | (function(this: any, a: string) {})("foo"); 75 | ~~~~~~~~ [fail] 76 | ((param: T): T => { 77 | return param; 78 | })(1); 79 | (function(): string { return "foo"; })(); 80 | (function cb(): string { return "foo"; })(); 81 | { 82 | let result = (function(): string { return "foo"; })(); 83 | result = ((function(): string { return "foo"; }))(); 84 | 85 | const v: string | false = (function(): string | false { return false; })(); 86 | ~~~~~~~~~~~~~~~~ [fail] 87 | const v2: true = (function(): true { return true; })(); 88 | ~~~~~~ [fail] 89 | } 90 | 91 | const myVerboseSignature: (a: number) => number = function(a: number): number { 92 | ~~~~~~~~~~~~~~~~~~~~~~~ [fail] 93 | ~~~~~~~~ [fail] 94 | return a; 95 | }; 96 | 97 | const evenWorkWithSignatures: {[key: string]: (a: string, b: number) => void} = { 98 | fn(a: string, b: number): void {}, 99 | ~~~~~~~~ [fail] 100 | ~~~~~~~~ [fail] 101 | foo(a: string, b: number) {}, 102 | ~~~~~~~~ [fail] 103 | ~~~~~~~~ [fail] 104 | bar(a: string, b) {}, 105 | ~~~~~~~~ [fail] 106 | baz(a): void {}, 107 | [foo1](a: string): void {}, 108 | ~~~~~~~~ [fail] 109 | noTypes(a, b) {}, 110 | 2(a: string, b: number) {} 111 | ~~~~~~~~ [fail] 112 | ~~~~~~~~ [fail] 113 | }; 114 | 115 | [1, "2", true].filter((e): e is string => typeof e === 'string'); 116 | take<(foo: any) => foo is string>((foo: any): foo is string => typeof foo === "string"); 117 | ~~~~~ [fail] 118 | take<((foo: any) => foo is string)|((foo: any) => boolean)>((foo: any): foo is string => typeof foo === "string"); 119 | ~~~~~ [fail] 120 | take<((foo: any) => foo is string)|((foo: any) => boolean)>((foo: any): boolean => typeof foo === "string"); 121 | ~~~~~ [fail] 122 | 123 | [fail]: type annotation is redundant 124 | -------------------------------------------------------------------------------- /test/rules/no-unnecessary-type-annotation/default/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strictNullChecks": true, 4 | "target": "esnext" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-unnecessary-type-annotation/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-unnecessary-type-annotation": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/declaration-export.d.ts.lint: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | ~~ [Import 'ts' is unused.] 3 | interface VariableInfo { 4 | ~~~~~~~~~~~~ [Interface 'VariableInfo' is unused.] 5 | domain: DeclarationDomain; 6 | } 7 | export interface VariableUse { 8 | domain: UsageDomain; 9 | } 10 | declare const enum DeclarationDomain { 11 | Namespace = 1, 12 | Type = 2, 13 | Value = 4, 14 | Any = 7, 15 | } 16 | export declare const enum UsageDomain { 17 | Namespace = 1, 18 | Type = 2, 19 | Value = 4, 20 | ValueOrNamespace = 5, 21 | Any = 7, 22 | TypeQuery = 8, 23 | } 24 | export declare function getUsageDomain(node: any): UsageDomain | undefined; 25 | export declare function getDeclarationDomain(node: any): DeclarationDomain | undefined; 26 | 27 | export {}; 28 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/declaration-global.d.ts.lint: -------------------------------------------------------------------------------- 1 | declare var v: number; 2 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/declaration.d.ts.lint: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | ~~ [Import 'ts' is unused.] 3 | interface VariableInfo { 4 | domain: DeclarationDomain; 5 | } 6 | export interface VariableUse { 7 | domain: UsageDomain; 8 | } 9 | declare const enum DeclarationDomain { 10 | Namespace = 1, 11 | Type = 2, 12 | Value = 4, 13 | Any = 7, 14 | } 15 | export declare const enum UsageDomain { 16 | Namespace = 1, 17 | Type = 2, 18 | Value = 4, 19 | ValueOrNamespace = 5, 20 | Any = 7, 21 | TypeQuery = 8, 22 | } 23 | export declare function getUsageDomain(node: any): UsageDomain | undefined; 24 | export declare function getDeclarationDomain(node: any): DeclarationDomain | undefined; 25 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/func.ts.lint: -------------------------------------------------------------------------------- 1 | function func1(x: number, y: number) { 2 | return x + y; 3 | } 4 | 5 | var func2 = () => { 6 | ~~~~~ [Variable 'func2' is unused.] 7 | // 8 | }; 9 | 10 | function func3({x, y}) { 11 | ~~~~~ [Function 'func3' is unused.] 12 | ~ [Variable 'y' is unused.] 13 | return func1(x, 2); 14 | } 15 | 16 | export function func4(x, y) { 17 | ~ [Parameter 'x' is unused.] 18 | return func1(y, 3); 19 | } 20 | 21 | declare function func5(): any; 22 | ~~~~~ [Function 'func5' is unused.] 23 | 24 | export default function (x, y = x) { 25 | ~ [Parameter 'y' is unused.] 26 | return 0; 27 | } 28 | 29 | (function foo(): typeof foo {}); 30 | (function foo(): typeof foo {})(); 31 | (function foo(): foo {}); 32 | (function foo(): foo {})(); 33 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/global.ts.lint: -------------------------------------------------------------------------------- 1 | let foo = 1; 2 | var bar = null; 3 | function fn(){} 4 | { 5 | let foo = 1; 6 | ~~~ [Variable 'foo' is unused.] 7 | var baz = {}; 8 | class Clazz {} 9 | ~~~~~ [Class 'Clazz' is unused.] 10 | } 11 | enum Test { 12 | foo, 13 | bar, 14 | } 15 | 16 | interface I {} 17 | 18 | class C {} 19 | 20 | namespace Foo.Bar { 21 | export namespace Baz { 22 | class LocalC {} 23 | ~~~~~~ [Class 'LocalC' is unused.] 24 | ~ [TypeParameter 'T' is unused.] 25 | export interface ExportI {} 26 | { 27 | export class Nested {} 28 | ~ [TypeParameter 'T' is unused.] 29 | } 30 | } 31 | namespace Nested { 32 | ~~~~~~ [Namespace 'Nested' is unused.] 33 | export interface ExportI {} 34 | ~ [TypeParameter 'T' is unused.] 35 | } 36 | 37 | namespace Another { 38 | ~~~~~~~ [Namespace 'Another' is unused.] 39 | interface F {} 40 | ~ [Interface 'F' is unused.] 41 | ~ [TypeParameter 'T' is unused.] 42 | class F {} 43 | ~ [Class 'F' is unused.] 44 | ~ [TypeParameter 'U' is unused.] 45 | 46 | interface P { 47 | ~ [Interface 'P' is unused.] 48 | foo: T; 49 | } 50 | class P {} 51 | ~ [Class 'P' is unused.] 52 | 53 | export interface P2 { 54 | foo: T, 55 | bar: U, 56 | } 57 | } 58 | namespace Another { 59 | ~~~~~~~ [Namespace 'Another' is unused.] 60 | export class P2 { 61 | baz(param: Z): Z {return param;} 62 | } 63 | } 64 | } 65 | 66 | declare module "foo" { 67 | class Foo {} 68 | ~ [TypeParameter 'T' is unused.] 69 | } 70 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/ignored.ts.lint: -------------------------------------------------------------------------------- 1 | function _fn(_param, {_destructured}) { 2 | ~~~ [Function '_fn' is unused.] 3 | ~~~~~~~~~~~~~ [Variable '_destructured' is unused.] 4 | } 5 | let {a: _a, ...rest} = {} as any; 6 | let {b: _b, c} = {} as any; 7 | ~~ [Variable '_b' is unused.] 8 | export = c; 9 | let [_c, ...d] = [] as any; 10 | ~~ [Variable '_c' is unused.] 11 | let {..._e} = rest; 12 | ~~ [Variable '_e' is unused.] 13 | 14 | let _i = 0; 15 | ~~ [Variable '_i' is only written and never read.] 16 | for (const _key in rest) 17 | ++_i; 18 | for (const _ of d) 19 | ++_i; 20 | 21 | try { 22 | } catch (e) { 23 | // e is unused but that's ok 24 | } 25 | 26 | let some_variable = 1; 27 | ~~~~~~~~~~~~~ [Variable 'some_variable' is unused.] 28 | class _Clazz { 29 | ~~~~~~ [Class '_Clazz' is unused.] 30 | constructor(private _foo, private bar, protected baz, public bas, readonly moar) {} 31 | } 32 | 33 | export type Foo = { 34 | [K in keyof T]: any 35 | } 36 | export type Bar = { 37 | [K in T]: T 38 | } 39 | export type Baz = { 40 | [K in keyof T]: T[K] 41 | } 42 | export type Bas = { 43 | ~ [TypeParameter 'T' is unused.] 44 | [K in keyof Object]: K 45 | } -------------------------------------------------------------------------------- /test/rules/no-unused/default/imports.js.lint: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | ~~ [Import 'ts' is unused.] 3 | import storageHelper, {removeItem as remove} from 'storage-helper'; 4 | ~~~~~~~~~~~~~ [Import 'storageHelper' is unused.] 5 | ~~~~~~ [Import 'remove' is unused.] 6 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/imports.ts.lint: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | ~~ [Import 'ts' is unused.] 3 | import storageHelper, {removeItem as remove} from 'storage-helper'; 4 | ~~~~~~~~~~~~~ [Import 'storageHelper' is unused.] 5 | ~~~~~~ [Import 'remove' is unused.] 6 | 7 | import Foo = Bar.Foo; 8 | ~~~ [Import 'Foo' is unused.] 9 | import fs = require('fs'); 10 | ~~ [Import 'fs' is unused.] 11 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/jsx-fragment.tsx.lint: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | let foo = <>; 4 | ~~~ [Variable 'foo' is unused.] 5 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/merge.ts.lint: -------------------------------------------------------------------------------- 1 | interface Foo {} 2 | ~~~ [Interface 'Foo' is unused.] 3 | class Foo {} 4 | 5 | export let foo = new Foo(); 6 | 7 | interface Bar {} 8 | class Bar {} 9 | enum Bar {} 10 | 11 | export let bar: Bar; 12 | 13 | interface Baz {} 14 | namespace Baz {} 15 | ~~~ [Namespace 'Baz' is unused.] 16 | 17 | export let baz: Baz; 18 | 19 | interface Bas { 20 | ~~~ [Interface 'Bas' is unused.] 21 | T: any; 22 | } 23 | enum Bas { 24 | T, 25 | } 26 | class Bas { 27 | ~~~ [Class 'Bas' is unused.] 28 | static T: any; 29 | } 30 | namespace Bas { 31 | export type T = any; 32 | } 33 | 34 | export let bas: Bas.T; 35 | 36 | interface Moar {} 37 | ~~~~ [Interface 'Moar' is unused.] 38 | class Moar {} 39 | namespace Moar {} 40 | 41 | export class EvenMoreMoar extends Moar {} 42 | 43 | interface Base {} 44 | class Base {} 45 | namespace Base {} 46 | ~~~~ [Namespace 'Base' is unused.] 47 | 48 | export interface Extended extends Base {} 49 | 50 | type T = object; 51 | ~ [TypeAlias 'T' is unused.] 52 | namespace T { 53 | export type V = any; 54 | } 55 | export let t: T.V; 56 | 57 | enum E {} 58 | ~ [Enum 'E' is unused.] 59 | namespace E {} 60 | ~ [Namespace 'E' is unused.] 61 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/mixed.lint: -------------------------------------------------------------------------------- 1 | // case 1 2 | const fs = require("fs"); 3 | 4 | namespace Foo { 5 | ~~~ [Namespace 'Foo' is unused.] 6 | const path = require("path"); 7 | 8 | console.log(fs); 9 | console.log(path); 10 | } 11 | 12 | // case 2 13 | class HelloWorld { 14 | constructor(public name: string) { } 15 | sayHello() { 16 | return `Hello, ${this.name}!`; 17 | } 18 | }; 19 | 20 | let hello = new HelloWorld("TSLint"); 21 | hello.sayHello(); 22 | 23 | // case 3 24 | import Bar = whatever.that.Foo; 25 | 26 | namespace some.module.blah { 27 | ~~~~ [Namespace 'some' is unused.] 28 | export class bar { 29 | private bar: Bar; 30 | constructor() { 31 | console.log(this.bar); 32 | } 33 | } 34 | } 35 | 36 | // case 4 37 | import DateTimeOpts = Intl.DateTimeFormatOptions; 38 | 39 | interface MyDateTimeOpts extends DateTimeOpts { 40 | timezoneOffset: number; 41 | } 42 | 43 | let opts: MyDateTimeOpts; 44 | console.log(opts.timezoneOffset - 1); 45 | 46 | import * as myLib from 'a'; 47 | export { myLib }; 48 | 49 | import foo from 'a'; 50 | const bar = {foo}; 51 | myFunc(bar); 52 | 53 | import a from "a"; 54 | export { a }; 55 | 56 | let foo, bar; 57 | export {foo, bar as baz}; 58 | 59 | (class T { 60 | foo(): T {} 61 | }); 62 | new (class T { 63 | foo(): T {} 64 | })(); 65 | (class T { 66 | foo(): T {} 67 | }); 68 | new (class T { 69 | foo(): T {} 70 | })(); 71 | 72 | declare global { 73 | interface Foo {} 74 | } 75 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/parameter.ts.lint: -------------------------------------------------------------------------------- 1 | let key; 2 | export function funxion(param: {[key: string]: string}) { 3 | return param[key]; 4 | } 5 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/react-self-closing.tsx.lint: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as React from 'react'; 3 | import React = require('react'); 4 | 5 | import react from 'react'; 6 | ~~~~~ [Import 'react' is unused.] 7 | import * as react from 'react'; 8 | ~~~~~ [Import 'react' is unused.] 9 | import react = require('react'); 10 | ~~~~~ [Import 'react' is unused.] 11 | 12 | let React, react; 13 | ~~~~~ [Variable 'React' is unused.] 14 | ~~~~~ [Variable 'react' is unused.] 15 | 16 | let foo = ; 17 | ~~~ [Variable 'foo' is unused.] 18 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/react-unused.tsx.lint: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | ~~~~~ [Import 'React' is unused.] 3 | import * as React from 'react'; 4 | ~~~~~ [Import 'React' is unused.] 5 | import React = require('react'); 6 | ~~~~~ [Import 'React' is unused.] 7 | 8 | import react from 'react'; 9 | ~~~~~ [Import 'react' is unused.] 10 | import * as react from 'react'; 11 | ~~~~~ [Import 'react' is unused.] 12 | import react = require('react'); 13 | ~~~~~ [Import 'react' is unused.] 14 | 15 | let React, react; 16 | ~~~~~ [Variable 'React' is unused.] 17 | ~~~~~ [Variable 'react' is unused.] 18 | 19 | let foo = null; 20 | ~~~ [Variable 'foo' is unused.] 21 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/react.ts.lint: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | ~~~~~ [Import 'React' is unused.] 3 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/react.tsx.lint: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as React from 'react'; 3 | import React = require('react'); 4 | 5 | import react from 'react'; 6 | ~~~~~ [Import 'react' is unused.] 7 | import * as react from 'react'; 8 | ~~~~~ [Import 'react' is unused.] 9 | import react = require('react'); 10 | ~~~~~ [Import 'react' is unused.] 11 | 12 | let React, react; 13 | ~~~~~ [Variable 'React' is unused.] 14 | ~~~~~ [Variable 'react' is unused.] 15 | 16 | let foo = ; 17 | ~~~ [Variable 'foo' is unused.] 18 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules":{ 4 | "no-unused": true 5 | }, 6 | "jsRules":{ 7 | "no-unused": true 8 | } 9 | } -------------------------------------------------------------------------------- /test/rules/no-unused/default/type-parameter.ts.lint: -------------------------------------------------------------------------------- 1 | export {} 2 | 3 | declare module "foo" { 4 | class Foo {} 5 | } 6 | 7 | export default class {} 8 | ~ [TypeParameter 'T' is unused.] 9 | 10 | declare global { 11 | namespace jest { 12 | interface Matchers { 13 | toEqualSomething(argument: string): object; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/used-in-declaration.ts.lint: -------------------------------------------------------------------------------- 1 | export {}; 2 | class Foo { 3 | ~~~ [Class 'Foo' is only used inside of its declaration.] 4 | getFoo(): Foo { 5 | return this; 6 | } 7 | } 8 | class Foo { 9 | ~~~ [Class 'Foo' is only used inside of its declaration.] 10 | private _foo: Foo; 11 | 12 | storeFoo(foo: Foo) { 13 | this._foo = foo; 14 | } 15 | } 16 | class Foo { 17 | ~~~ [Class 'Foo' is only used inside of its declaration.] 18 | static createFoo(): Foo { 19 | return new Foo(); 20 | } 21 | 22 | foo(foo: Foo) { 23 | foo.storeFoo(Foo.createFoo()) 24 | } 25 | } 26 | 27 | function cb(node): void { 28 | ~~ [Function 'cb' is only used inside of its declaration.] 29 | return ts.forEachChild(node, cb); 30 | } 31 | 32 | let fn = (i) => fn(i + 1); 33 | ~~ [Variable 'fn' is only written or used inside of its declaration.] 34 | fn = () => {}; 35 | 36 | function self(): typeof self { 37 | ~~~~ [Function 'self' is only used inside of its declaration.] 38 | return self; 39 | } 40 | 41 | // should never fail 42 | let connection = connect(() => { 43 | doStuffWithConnection(connection); 44 | }); 45 | 46 | let bogus: typeof bogus = 1; 47 | ~~~~~ [Variable 'bogus' is only used inside of its declaration.] 48 | let bogus2: typeof bogus2; 49 | ~~~~~~ [Variable 'bogus2' is only used inside of its declaration.] 50 | 51 | let func = condition ? () => func(condition) : (param) => func(param); 52 | ~~~~ [Variable 'func' is only used inside of its declaration.] 53 | 54 | -------------------------------------------------------------------------------- /test/rules/no-unused/default/write-only.ts.lint: -------------------------------------------------------------------------------- 1 | export {}; 2 | let foo = 1; 3 | ~~~ [Variable 'foo' is only written and never read.] 4 | let baz = 1; 5 | ~~~ [Variable 'baz' is only written and never read.] 6 | for (let i;; ++i) { 7 | foo++; 8 | console.log(baz = i); 9 | } 10 | let bar = 1; 11 | ~~~ [Variable 'bar' is only written and never read.] 12 | bar = bar + 1; 13 | bar = -(bar!); 14 | bar = 1 + bar; 15 | 16 | let a = "moar"; 17 | 18 | let bas = "."; 19 | ~~~ [Variable 'bas' is only written and never read.] 20 | bas += bas; 21 | bas = "a" + bas + "b"; 22 | bas = a + bas; 23 | bas = `?${bas + "moar"}!`; 24 | bas = {prop: bas, bas}; 25 | bas = {...(bas)}; 26 | 27 | let obj = {foo: 1}; 28 | obj.foo = obj.foo + 1; 29 | 30 | let b = true; 31 | ~ [Variable 'b' is only written and never read.] 32 | for (let i = 0; i < 5; ++i) 33 | b = !b; 34 | b = b && confirm("foo"); 35 | b = b === false; 36 | 37 | let someVar = someFunc(); 38 | ~~~~~~~ [Variable 'someVar' is only written and never read.] 39 | someVar = someVar instanceof Object ? a : someVar || null 40 | someVar = someVar && a; 41 | 42 | let result = ""; 43 | ~~~~~~ [Variable 'result' is only written and never read.] 44 | let x = 0; 45 | while (true) 46 | result = `${x+=1}`; 47 | -------------------------------------------------------------------------------- /test/rules/no-unused/ignore-imports/declaration.d.ts.lint: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | ~~ [Import 'ts' is unused.] 3 | -------------------------------------------------------------------------------- /test/rules/no-unused/ignore-imports/imports.js.lint: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | ~~ [Import 'ts' is unused.] 3 | import storageHelper, {removeItem as remove} from 'storage-helper'; 4 | ~~~~~~~~~~~~~ [Import 'storageHelper' is unused.] 5 | ~~~~~~ [Import 'remove' is unused.] 6 | -------------------------------------------------------------------------------- /test/rules/no-unused/ignore-imports/imports.ts.lint: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import storageHelper, {removeItem as remove} from 'storage-helper'; 3 | 4 | import Foo = Bar.Foo; 5 | ~~~ [Import 'Foo' is unused.] 6 | import fs = require('fs'); 7 | -------------------------------------------------------------------------------- /test/rules/no-unused/ignore-imports/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules":{ 4 | "no-unused": [true, "ignore-imports"] 5 | }, 6 | "jsRules":{ 7 | "no-unused": [true, "ignore-imports"] 8 | } 9 | } -------------------------------------------------------------------------------- /test/rules/no-unused/unused-catch-binding/test.ts.lint: -------------------------------------------------------------------------------- 1 | try { 2 | foo(); 3 | } catch(err) {} 4 | ~~~ [Variable 'err' is unused.] 5 | 6 | try { 7 | foo(); 8 | } catch(err) { 9 | console.log(err); 10 | } 11 | -------------------------------------------------------------------------------- /test/rules/no-unused/unused-catch-binding/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules":{ 4 | "no-unused": [true, "unused-catch-binding"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-unused/unused-named-expression/test.ts.fix: -------------------------------------------------------------------------------- 1 | (function() {}); 2 | (function() {})(); 3 | 4 | (function foo(): typeof foo {}); 5 | (function foo(): typeof foo {})(); 6 | 7 | (class {}); 8 | new (class {})(); 9 | 10 | (class Unused { 11 | foo(): Unused {} 12 | }); 13 | new (class Unused { 14 | foo(): Unused {} 15 | })(); 16 | -------------------------------------------------------------------------------- /test/rules/no-unused/unused-named-expression/test.ts.lint: -------------------------------------------------------------------------------- 1 | (function foo() {}); 2 | ~~~ [Function 'foo' is never used by its name. Convert it to an anonymous function expression.] 3 | (function foo() {})(); 4 | ~~~ [Function 'foo' is never used by its name. Convert it to an anonymous function expression.] 5 | 6 | (function foo(): typeof foo {}); 7 | (function foo(): typeof foo {})(); 8 | 9 | (class Unused {}); 10 | ~~~~~~ [Class 'Unused' is never used by its name. Convert it to an anonymous class expression.] 11 | new (class Unused {})(); 12 | ~~~~~~ [Class 'Unused' is never used by its name. Convert it to an anonymous class expression.] 13 | 14 | (class Unused { 15 | foo(): Unused {} 16 | }); 17 | new (class Unused { 18 | foo(): Unused {} 19 | })(); 20 | -------------------------------------------------------------------------------- /test/rules/no-unused/unused-named-expression/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules":{ 4 | "no-unused": [true, "unused-function-expression-name", "unused-class-expression-name"] 5 | } 6 | } -------------------------------------------------------------------------------- /test/rules/no-var-before-return/allow-destructuring/test.ts.lint: -------------------------------------------------------------------------------- 1 | (bar) => { 2 | let baz = bar; 3 | return baz; 4 | ~~~ [fail] 5 | } 6 | 7 | function foo(bar) { 8 | let baz = bar; 9 | return baz; 10 | ~~~ [fail] 11 | } 12 | 13 | function foo(bar) { 14 | let {baz} = bar; 15 | return baz; 16 | } 17 | 18 | [fail]: don't declare variable baz to return it immediately 19 | -------------------------------------------------------------------------------- /test/rules/no-var-before-return/allow-destructuring/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-var-before-return": [true, "allow-destructuring"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/no-var-before-return/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | function foo(bar) { 2 | return bar; 3 | } 4 | 5 | function foo(bar) { 6 | if (bar) { 7 | let baz = bar; 8 | return baz; 9 | ~~~ [fail] 10 | } 11 | } 12 | 13 | function foo(bar) { 14 | let baz = bar; 15 | if (bar) 16 | return baz; 17 | } 18 | 19 | (baz) => baz; 20 | 21 | (baz) => return baz; 22 | 23 | (bar) => { 24 | let baz = bar; 25 | return baz; 26 | ~~~ [fail] 27 | } 28 | 29 | function foo(bar) { 30 | let baz = bar; 31 | return baz; 32 | ~~~ [fail] 33 | } 34 | 35 | function foo(bar) { 36 | let baz = bar, foo = baz; 37 | return baz; 38 | } 39 | 40 | function foo(bar) { 41 | let baz = bar, foo = ajaff(bar); 42 | return baz; 43 | } 44 | 45 | function foo(bar) { 46 | let {baz} = bar; 47 | return baz; 48 | ~~~ [fail] 49 | } 50 | 51 | function foo(bar) { 52 | let {baz = 1} = bar; 53 | return baz; 54 | } 55 | 56 | function foo(bar) { 57 | let {baz = void 0} = bar; 58 | return baz; 59 | ~~~ [fail] 60 | } 61 | 62 | function foo(bar) { 63 | let {foo: baz} = bar; 64 | return baz; 65 | ~~~ [fail] 66 | } 67 | 68 | function foo(bar) { 69 | let {foo: {bar: baz}} = bar; 70 | return baz; 71 | ~~~ [fail] 72 | } 73 | 74 | function foo(bar) { 75 | let [baz] = bar; 76 | return baz; 77 | } 78 | 79 | function foo(bar) { 80 | let [baz = undefined] = bar; 81 | return baz; 82 | } 83 | 84 | function foo(bar) { 85 | let [baz = 1] = bar; 86 | return baz; 87 | } 88 | 89 | function foo(bar) { 90 | let [[baz], bas] = bar; 91 | return baz; 92 | } 93 | 94 | function foo(bar) { 95 | let [{bar: [baz]}] = bar; 96 | return baz; 97 | } 98 | 99 | function foo(bar) { 100 | let foo = bar, baz = bar; 101 | return baz; 102 | ~~~ [fail] 103 | } 104 | 105 | function foo(bar) { 106 | let {foo: {baz = undefined}} = bar; 107 | return baz; 108 | ~~~ [fail] 109 | } 110 | 111 | function foo(bar) { 112 | let {foo: {baz = 1}} = bar; 113 | return baz; 114 | } 115 | 116 | function foo(bar) { 117 | let {foo, [foo]: baz} = bar; 118 | return baz; 119 | } 120 | 121 | function foo(bar) { 122 | let {foo, [tmp]: baz} = bar; 123 | return baz; 124 | ~~~ [fail] 125 | } 126 | 127 | function foo(bar) { 128 | let {foo: {foo}, [foo]: baz} = bar; 129 | return baz; 130 | } 131 | 132 | function foo(bar) { 133 | let [, ...baz] = bar; 134 | return baz; 135 | } 136 | 137 | function foo(bar) { 138 | let {foo, ...baz} = bar; 139 | return baz; 140 | } 141 | 142 | function foo(bar) { 143 | let {foo, bas, baz: [ajaff, {moar}]} = bar; 144 | return baz; 145 | } 146 | 147 | class Test { 148 | foo(bar) { 149 | let baz = bar; 150 | return baz; 151 | ~~~ [fail] 152 | } 153 | 154 | get baz() { 155 | let baz = bar; 156 | return baz; 157 | ~~~ [fail] 158 | } 159 | } 160 | 161 | function foo() { 162 | let foo = 1; 163 | debugger; 164 | return foo; 165 | } 166 | 167 | function foo() { 168 | let foo = 1; 169 | return; 170 | } 171 | 172 | function foo() { 173 | let foo = 1; 174 | return foo + 1; 175 | } 176 | 177 | function foo(bar) { 178 | let {...baz} = bar; 179 | return baz; 180 | ~~~ [fail] 181 | } 182 | 183 | function foo(bar) { 184 | let [...baz] = bar; 185 | return baz; 186 | ~~~ [fail] 187 | } 188 | 189 | function foo(bar) { 190 | let {[bar()]: baz} = {}; 191 | return baz; // allow this for now 192 | } 193 | 194 | function foo(bar) { 195 | let [{...baz}] = bar; 196 | return bar; 197 | } 198 | 199 | function foo(bar) { 200 | let [[...baz]] = bar; 201 | return baz; 202 | } 203 | 204 | function foo(bar) { 205 | let {foo: [foo], baz} = bar; 206 | return baz; 207 | ~~~ [fail] 208 | } 209 | 210 | function foo(bar) { 211 | let {foo, [foo]: {baz}} = bar; 212 | return baz; 213 | } 214 | 215 | function foo(bar) { 216 | let {foo, [foo]: {bas}, baz} = bar; 217 | return baz; 218 | ~~~ [fail] 219 | } 220 | 221 | function foo(bar) { 222 | let {['bar']: baz} = bar; 223 | return baz; 224 | ~~~ [fail] 225 | } 226 | 227 | function foo(bar) { 228 | var baz = { 229 | fn() {console.log(baz);} 230 | }; 231 | return baz; 232 | } 233 | 234 | function foo(bar) { 235 | var baz = { 236 | doStuff, 237 | }; 238 | return baz; 239 | function doStuff() { 240 | return baz.doStuff(); 241 | } 242 | } 243 | 244 | [fail]: don't declare variable baz to return it immediately 245 | -------------------------------------------------------------------------------- /test/rules/no-var-before-return/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "no-var-before-return": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/object-shorthand-properties-first/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | foo = {foo, bar}; 2 | foo = {foo, bar: baz}; 3 | foo = {foo: bar, baz}; 4 | ~~~ [fail] 5 | foo = {foo: foo, bar, baz: baz, bas}; 6 | ~~~ [fail] 7 | ~~~ [fail] 8 | foo = { 9 | foo, 10 | bar: bar, 11 | baz, 12 | ~~~ [fail] 13 | bas: bas, 14 | } 15 | 16 | foo = { 17 | bar: bar, 18 | baz, 19 | ~~~ [fail] 20 | ...bar, 21 | bas, 22 | } 23 | 24 | [fail]: shorthand properties should come first -------------------------------------------------------------------------------- /test/rules/object-shorthand-properties-first/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "object-shorthand-properties-first": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/oddness-check/default/test.ts.fix: -------------------------------------------------------------------------------- 1 | foo & 1; 2 | foo&1; 3 | bar%1; 4 | bar % -2; 5 | foo % bar; 6 | 2 % bar; -------------------------------------------------------------------------------- /test/rules/oddness-check/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | foo % 2; 2 | ~~~ [Modulus 2 can be replaced with & 1] 3 | foo%2; 4 | ~~ [Modulus 2 can be replaced with & 1] 5 | bar%1; 6 | bar % -2; 7 | foo % bar; 8 | 2 % bar; -------------------------------------------------------------------------------- /test/rules/oddness-check/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "oddness-check": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/parameter-properties/all-or-none/test.ts.lint: -------------------------------------------------------------------------------- 1 | class Foo { 2 | constructor(foo: string, public bar: number, readonly baz?) {} 3 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [don't mix parameter properties with regular parameters] 4 | } 5 | 6 | class Bar { 7 | constructor() {} 8 | } 9 | 10 | class Baz { 11 | constructor(bas) {} 12 | } 13 | 14 | class Bas { 15 | constructor(readonly baz) {} 16 | } 17 | 18 | class Quux { 19 | constructor(readonly foo, private bar, public baz, bas) {} 20 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [don't mix parameter properties with regular parameters] 21 | } -------------------------------------------------------------------------------- /test/rules/parameter-properties/all-or-none/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "parameter-properties": [ 5 | true, 6 | "all-or-none" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/rules/parameter-properties/leading/test.ts.lint: -------------------------------------------------------------------------------- 1 | class Foo { 2 | constructor(foo: string, public bar: number, readonly baz?) {} 3 | ~~~~~~~~~~~~~~~~~~ [fail] 4 | ~~~~~~~~~~~~~ [fail] 5 | } 6 | 7 | class Bar { 8 | constructor() {} 9 | } 10 | 11 | class Baz { 12 | constructor(bas) {} 13 | } 14 | 15 | class Bas { 16 | constructor(readonly baz) {} 17 | } 18 | 19 | class Quux { 20 | constructor(protected foo, bar) {} 21 | } 22 | 23 | class Ajaff { 24 | constructor(private foo, bar, public baz) {} 25 | ~~~~~~~~~~ [fail] 26 | } 27 | 28 | [fail]: parameter properties must precede regular parameters -------------------------------------------------------------------------------- /test/rules/parameter-properties/leading/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "parameter-properties": [ 5 | true, 6 | "leading" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/rules/parameter-properties/member-access/test.ts.lint: -------------------------------------------------------------------------------- 1 | class Foo { 2 | constructor(foo: string, public bar: number, readonly baz?) {} 3 | ~~~~~~~~~~~~~ [fail] 4 | } 5 | 6 | class Bar { 7 | constructor() {} 8 | } 9 | 10 | class Baz { 11 | constructor(bas) {} 12 | } 13 | 14 | class Bas { 15 | constructor(readonly baz) {} 16 | ~~~~~~~~~~~~ [fail] 17 | } 18 | 19 | class Quux { 20 | constructor(protected readonly foo, bar) {} 21 | } 22 | 23 | class Ajaff { 24 | constructor(private foo, bar, public baz) {} 25 | } 26 | 27 | [fail]: parameter property must have access modifier -------------------------------------------------------------------------------- /test/rules/parameter-properties/member-access/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "parameter-properties": [ 5 | true, 6 | "member-access" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/rules/parameter-properties/readonly/test.ts.lint: -------------------------------------------------------------------------------- 1 | class Foo { 2 | constructor(foo: string, public bar: number, readonly baz?) {} 3 | ~~~~~~~~~~~~~~~~~~ [fail] 4 | } 5 | 6 | class Bar { 7 | constructor() {} 8 | } 9 | 10 | class Baz { 11 | constructor(bas) {} 12 | } 13 | 14 | class Bas { 15 | constructor(readonly baz) {} 16 | } 17 | 18 | class Quux { 19 | constructor(protected readonly foo, bar) {} 20 | } 21 | 22 | class Ajaff { 23 | constructor(private foo, bar, public baz) {} 24 | ~~~~~~~~~~~ [fail] 25 | ~~~~~~~~~~ [fail] 26 | } 27 | 28 | [fail]: parameter property must be readonly -------------------------------------------------------------------------------- /test/rules/parameter-properties/readonly/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "parameter-properties": [ 5 | true, 6 | "readonly" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/rules/parameter-properties/trailing/test.ts.lint: -------------------------------------------------------------------------------- 1 | class Foo { 2 | constructor(foo: string, public bar: number, readonly baz?) {} 3 | } 4 | 5 | class Bar { 6 | constructor() {} 7 | } 8 | 9 | class Baz { 10 | constructor(bas) {} 11 | } 12 | 13 | class Bas { 14 | constructor(readonly baz) {} 15 | } 16 | 17 | class Quux { 18 | constructor(protected foo, bar) {} 19 | ~~~ [fail] 20 | } 21 | 22 | class Ajaff { 23 | constructor(private foo, bar, public baz) {} 24 | ~~~ [fail] 25 | } 26 | 27 | [fail]: regular parameters must precede parameter properties 28 | -------------------------------------------------------------------------------- /test/rules/parameter-properties/trailing/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "parameter-properties": [ 5 | true, 6 | "trailing" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/rules/prefer-const-enum/default/global.ts.lint: -------------------------------------------------------------------------------- 1 | enum Foo {} 2 | enum Bar { 3 | foo, 4 | } 5 | 6 | { 7 | enum Baz {} 8 | ~~~~~~~~ [Enum 'Baz' can be a 'const enum'.] 9 | const enum Bas {} 10 | } 11 | -------------------------------------------------------------------------------- /test/rules/prefer-const-enum/default/merged.ts.lint: -------------------------------------------------------------------------------- 1 | export {}; 2 | enum Foo {} 3 | namespace Foo {} 4 | -------------------------------------------------------------------------------- /test/rules/prefer-const-enum/default/string.ts.lint: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | const enum Base { 4 | Foo = "foo", 5 | Bar = Foo, 6 | } 7 | 8 | enum Parens { 9 | ~~~~~~~~~~~ [fail % ('Parens')] 10 | Foo = ((Base.Foo)), 11 | } 12 | 13 | enum Literal { 14 | Foo = +("Foo"), 15 | } 16 | 17 | enum StringEnumInitializer { 18 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ [fail % ('StringEnumInitializer')] 19 | Foo = Base.Foo, 20 | } 21 | 22 | enum StringConcat { 23 | FooBar = Base.Foo + Base.Bar, 24 | } 25 | 26 | enum StringConcat2 { 27 | FooBar = Base.Foo + "Bar", 28 | } 29 | 30 | enum StringArithmetic { 31 | Foo = -(Base.Bar), 32 | } 33 | 34 | [fail]: Enum '%s' can be a 'const enum'. 35 | -------------------------------------------------------------------------------- /test/rules/prefer-const-enum/default/test.ts.fix: -------------------------------------------------------------------------------- 1 | const enum Empty {} 2 | 3 | const enum Foo { 4 | bar, 5 | baz, 6 | } 7 | bar = Foo.bar; 8 | baz = Foo['baz']; 9 | let f: Foo; 10 | let b: Foo.bar; 11 | 12 | const enum Foo { 13 | foo = 2, 14 | } 15 | 16 | enum Foo2 { 17 | bar, 18 | baz = foo, 19 | } 20 | 21 | enum Foo2 { 22 | foo = 3, 23 | } 24 | 25 | const enum Bar { 26 | foo = 1, 27 | bar = 1 + 1, 28 | baz = 1 << 1, 29 | } 30 | 31 | const enum ConstBar { 32 | } 33 | 34 | const enum Baz { 35 | 'foo', 36 | bar = foo, 37 | ['baz'] = Baz.bar, 38 | bas = Baz['baz'], 39 | quux = bas + Baz.baz | Baz['foo'] + ~~bar & (Foo.bar - Foo2.bar), 40 | } 41 | 42 | enum Baz2 { 43 | foo = Foo2.baz, 44 | } 45 | 46 | enum Bas { 47 | foo, 48 | } 49 | 50 | enum Bas2 { 51 | foo = Bas[foo], 52 | } 53 | 54 | export enum ExportedEnum {} 55 | 56 | const enum Split { 57 | Foo, 58 | Bar, 59 | } 60 | 61 | const enum Split { 62 | Baz = Foo, 63 | } 64 | 65 | enum NumberAccess { 66 | Foo, 67 | } 68 | NumberAccess[0]; 69 | 70 | enum Assigned {} 71 | foo = Assigned; 72 | 73 | enum TemplateAccess { 74 | Bar, 75 | } 76 | foo = TemplateAccess[`Bar`]; 77 | 78 | enum Parameter {} 79 | foo(Parameter); 80 | 81 | enum Paren { 82 | Bar, 83 | Baz = (Paren).Bar, 84 | } 85 | 86 | enum Paren2 { 87 | Bar, 88 | } 89 | foo = (Paren2).Bar; 90 | 91 | enum Exported {} 92 | export {Exported}; 93 | 94 | const enum StringValued { 95 | Foo = "FOO", 96 | Bar = 'Bar', 97 | Baz = Foo, 98 | } 99 | 100 | const enum Mixed { 101 | foo = StringValued.Foo, 102 | bar = Foo.bar, 103 | } 104 | 105 | enum TypeOf {} 106 | let t: typeof TypeOf; 107 | 108 | enum TypeOfMember { 109 | member, 110 | } 111 | let m: typeof TypeOfMember.member; 112 | 113 | enum NonConst { 114 | foo = "foo", 115 | bar = `foo = ${foo}`, 116 | } 117 | enum NonConst2 { 118 | foo, 119 | bar, 120 | baz, 121 | bas = foo ? bar : baz, 122 | } 123 | enum NonConst3 { 124 | foo, 125 | bar, 126 | baz = foo && bar, 127 | bas = foo || bar, 128 | } 129 | 130 | enum UnknownInitializer { 131 | Foo = _FooBarBaz_, 132 | Bar = _FooBarBaz_.Bar, 133 | Baz = _FooBarBaz_['Foo'], 134 | } 135 | 136 | -------------------------------------------------------------------------------- /test/rules/prefer-const-enum/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | enum Empty {} 2 | ~~~~~~~~~~ [fail % ('Empty')] 3 | 4 | enum Foo { 5 | ~~~~~~~~ [fail % ('Foo')] 6 | bar, 7 | baz, 8 | } 9 | bar = Foo.bar; 10 | baz = Foo['baz']; 11 | let f: Foo; 12 | let b: Foo.bar; 13 | 14 | enum Foo { 15 | ~~~~~~~~ [fail % ('Foo')] 16 | foo = 2, 17 | } 18 | 19 | enum Foo2 { 20 | bar, 21 | baz = foo, 22 | } 23 | 24 | enum Foo2 { 25 | foo = 3, 26 | } 27 | 28 | enum Bar { 29 | ~~~~~~~~ [fail % ('Bar')] 30 | foo = 1, 31 | bar = 1 + 1, 32 | baz = 1 << 1, 33 | } 34 | 35 | const enum ConstBar { 36 | } 37 | 38 | enum Baz { 39 | ~~~~~~~~ [fail % ('Baz')] 40 | 'foo', 41 | bar = foo, 42 | ['baz'] = Baz.bar, 43 | bas = Baz['baz'], 44 | quux = bas + Baz.baz | Baz['foo'] + ~~bar & (Foo.bar - Foo2.bar), 45 | } 46 | 47 | enum Baz2 { 48 | foo = Foo2.baz, 49 | } 50 | 51 | enum Bas { 52 | foo, 53 | } 54 | 55 | enum Bas2 { 56 | foo = Bas[foo], 57 | } 58 | 59 | export enum ExportedEnum {} 60 | 61 | enum Split { 62 | ~~~~~~~~~~ [fail % ('Split')] 63 | Foo, 64 | Bar, 65 | } 66 | 67 | enum Split { 68 | ~~~~~~~~~~ [fail % ('Split')] 69 | Baz = Foo, 70 | } 71 | 72 | enum NumberAccess { 73 | Foo, 74 | } 75 | NumberAccess[0]; 76 | 77 | enum Assigned {} 78 | foo = Assigned; 79 | 80 | enum TemplateAccess { 81 | Bar, 82 | } 83 | foo = TemplateAccess[`Bar`]; 84 | 85 | enum Parameter {} 86 | foo(Parameter); 87 | 88 | enum Paren { 89 | Bar, 90 | Baz = (Paren).Bar, 91 | } 92 | 93 | enum Paren2 { 94 | Bar, 95 | } 96 | foo = (Paren2).Bar; 97 | 98 | enum Exported {} 99 | export {Exported}; 100 | 101 | enum StringValued { 102 | ~~~~~~~~~~~~~~~~~ [fail % ('StringValued')] 103 | Foo = "FOO", 104 | Bar = 'Bar', 105 | Baz = Foo, 106 | } 107 | 108 | enum Mixed { 109 | ~~~~~~~~~~ [fail % ('Mixed)] 110 | foo = StringValued.Foo, 111 | bar = Foo.bar, 112 | } 113 | 114 | enum TypeOf {} 115 | let t: typeof TypeOf; 116 | 117 | enum TypeOfMember { 118 | member, 119 | } 120 | let m: typeof TypeOfMember.member; 121 | 122 | enum NonConst { 123 | foo = "foo", 124 | bar = `foo = ${foo}`, 125 | } 126 | enum NonConst2 { 127 | foo, 128 | bar, 129 | baz, 130 | bas = foo ? bar : baz, 131 | } 132 | enum NonConst3 { 133 | foo, 134 | bar, 135 | baz = foo && bar, 136 | bas = foo || bar, 137 | } 138 | 139 | enum UnknownInitializer { 140 | Foo = _FooBarBaz_, 141 | Bar = _FooBarBaz_.Bar, 142 | Baz = _FooBarBaz_['Foo'], 143 | } 144 | 145 | [fail]: Enum '%s' can be a 'const enum'. 146 | -------------------------------------------------------------------------------- /test/rules/prefer-const-enum/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "prefer-const-enum": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/rules/prefer-while/default/test.ts.fix: -------------------------------------------------------------------------------- 1 | while (true); 2 | 3 | while (true); 4 | 5 | while (false); 6 | 7 | while (false); 8 | 9 | for (;false;++i); 10 | 11 | for (i = 1; --i;); 12 | 13 | for (i = 1; i, --i); 14 | 15 | -------------------------------------------------------------------------------- /test/rules/prefer-while/default/test.ts.lint: -------------------------------------------------------------------------------- 1 | for (;;); 2 | ~~~~~~~~ [fail] 3 | 4 | for ( ; ; ); 5 | ~~~~~~~~~~~ [fail] 6 | 7 | for (;false;); 8 | ~~~~~~~~~~~~~ [fail] 9 | 10 | for ( ; false ; ); 11 | ~~~~~~~~~~~~~~~~~ [fail] 12 | 13 | for (;false;++i); 14 | 15 | for (i = 1; --i;); 16 | 17 | for (i = 1; i, --i); 18 | 19 | [fail]: Prefer `while` loops instead of `for` loops without an initializer and incrementor. 20 | -------------------------------------------------------------------------------- /test/rules/prefer-while/default/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": "../../../../rules", 3 | "rules": { 4 | "prefer-while": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "removeComments": true, 6 | "inlineSourceMap": true, 7 | "noImplicitAny": true, 8 | "noImplicitThis": true, 9 | "suppressImplicitAnyIndexErrors": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "strictNullChecks": true, 13 | "stripInternal": true, 14 | "lib": ["es6"], 15 | "skipLibCheck": true, 16 | "importHelpers": true, 17 | "plugins": [ 18 | {"name": "@fimbul/mithotyn", "displayErrorsAsWarnings": true} 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "./rules" 4 | ], 5 | "rules": { 6 | "adjacent-overload-signatures": true, 7 | "align": [ 8 | true, 9 | "statements", 10 | "parameters", 11 | "arguments", 12 | "elements", 13 | "members" 14 | ], 15 | "array-type": [ 16 | true, 17 | "array-simple" 18 | ], 19 | "arrow-parens": true, 20 | "arrow-return-shorthand": true, 21 | "ban-comma-operator": true, 22 | "class-name": true, 23 | "comment-format": [true, "check-space"], 24 | "eofline": true, 25 | "forin": true, 26 | "indent": [true, "spaces"], 27 | "max-line-length": [true, 140], 28 | "member-access": true, 29 | "new-parens": true, 30 | "no-arg": true, 31 | "no-conditional-assignment": true, 32 | "no-consecutive-blank-lines": [ 33 | true, 34 | 1 35 | ], 36 | "no-console": [true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-construct": true, 44 | "no-debugger": true, 45 | "no-default-export": true, 46 | "no-duplicate-imports": true, 47 | "no-duplicate-switch-case": true, 48 | "no-empty-interface": true, 49 | "no-eval": true, 50 | "no-implicit-dependencies": true, 51 | "no-import-side-effect": true, 52 | "no-invalid-this": true, 53 | "no-inferrable-types": [ 54 | true, 55 | "ignore-params" 56 | ], 57 | "no-irregular-whitespace": true, 58 | "no-null-keyword": true, 59 | "no-require-imports": true, 60 | "no-return-await": true, 61 | "no-shadowed-variable": true, 62 | "no-sparse-arrays": true, 63 | "no-string-literal": true, 64 | "no-submodule-imports": true, 65 | "no-switch-case-fall-through": true, 66 | "no-this-assignment": true, 67 | "no-trailing-whitespace": true, 68 | "no-unnecessary-callback-wrapper": true, 69 | "no-unnecessary-initializer": true, 70 | "no-unsafe-finally": true, 71 | "no-unused-expression": true, 72 | "no-var-keyword": true, 73 | "number-literal-format": true, 74 | "object-literal-shorthand": true, 75 | "one-line": [true, 76 | "check-open-brace", 77 | "check-catch", 78 | "check-else", 79 | "check-finally", 80 | "check-whitespace" 81 | ], 82 | "one-variable-per-declaration": [ 83 | true, 84 | "ignore-for-loop" 85 | ], 86 | "only-arrow-functions": [ 87 | true, 88 | "allow-named-functions" 89 | ], 90 | "prefer-const": true, 91 | "prefer-for-of": true, 92 | "prefer-function-over-method": true, 93 | "prefer-object-spread": true, 94 | "prefer-switch": [true, {"min-cases": 3}], 95 | "quotemark": [ 96 | true, 97 | "single", 98 | "avoid-escape" 99 | ], 100 | "radix": true, 101 | "semicolon": [true, "always"], 102 | "space-before-function-paren": [ 103 | true, 104 | { 105 | "anonymous": false, 106 | "named": false, 107 | "asyncArrow": true, 108 | "method": false, 109 | "constructor": false 110 | } 111 | ], 112 | "space-within-parens": [true, 0], 113 | "switch-final-break": true, 114 | "trailing-comma": [true, { 115 | "singleline": "never", 116 | "multiline": "always" 117 | }], 118 | "triple-equals": [true, "allow-null-check"], 119 | "typedef-whitespace": [true, { 120 | "call-signature": "nospace", 121 | "index-signature": "nospace", 122 | "parameter": "nospace", 123 | "property-declaration": "nospace", 124 | "variable-declaration": "nospace" 125 | }], 126 | "unified-signatures": true, 127 | "use-isnan": true, 128 | "variable-name": [ 129 | true, 130 | "ban-keywords" 131 | ], 132 | "whitespace": [true, 133 | "check-branch", 134 | "check-decl", 135 | "check-operator", 136 | "check-separator", 137 | "check-type", 138 | "check-type-operator", 139 | "check-rest-spread" 140 | ], 141 | /* custom rules */ 142 | "no-unused": [ 143 | true, 144 | "unused-class-expression-name", 145 | "unused-function-expression-name", 146 | "unused-catch-binding" 147 | ], 148 | "ext-curly": [ 149 | true, 150 | "else", 151 | "nested-if-else", 152 | "braced-child" 153 | ], 154 | "naming-convention": [ 155 | true, 156 | {"type": "default", "format": "camelCase", "leadingUnderscore": "forbid", "trailingUnderscore": "forbid"}, 157 | {"type": "variable", "modifiers": ["global", "const"], "format": ["camelCase", "UPPER_CASE"]}, 158 | {"type": "parameter", "modifiers": "unused", "leadingUnderscore": "allow"}, 159 | {"type": "member", "modifiers": "private", "leadingUnderscore": "require"}, 160 | {"type": "member", "modifiers": "protected", "leadingUnderscore": "require"}, 161 | {"type": "property", "modifiers": ["public", "static", "const"], "format": "UPPER_CASE"}, 162 | {"type": "type", "format": "PascalCase"}, 163 | {"type": "class", "modifiers": "abstract", "prefix": "Abstract"}, 164 | {"type": "interface", "prefix": "I"}, 165 | {"type": "genericTypeParameter", "regex": "^[A-Z]$"}, 166 | {"type": "enumMember", "format": "PascalCase"} 167 | ], 168 | "no-as-type-assertion": true, 169 | "no-accessor-recursion": true, 170 | "no-collapsible-if": true, 171 | "no-return-undefined": [ 172 | true, 173 | "allow-void-expression" 174 | ], 175 | "no-unnecessary-else": true, 176 | "no-unnecessary-type-annotation": true, 177 | "no-var-before-return": true, 178 | "object-shorthand-properties-first": true, 179 | "parameter-properties": [ 180 | true, 181 | "leading", 182 | "member-access" 183 | ], 184 | "prefer-const-enum": true, 185 | "prefer-while": true 186 | } 187 | } 188 | --------------------------------------------------------------------------------