{"id":23386131,"url":"https://github.com/jens-duttke/tslint-filter","last_synced_at":"2025-04-08T12:16:20.289Z","repository":{"id":34030051,"uuid":"166014792","full_name":"jens-duttke/tslint-filter","owner":"jens-duttke","description":"Suppress and extend TSLint warnings, before they get returned to the console or your code editor","archived":false,"fork":false,"pushed_at":"2023-03-04T03:30:29.000Z","size":112,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-14T12:36:53.560Z","etag":null,"topics":["errors","exception","extend-rules","failures","filter","ignore","modify","predefined-rule-wrappers","regular-expression","rules","tslint","tslint-filter","tslint-rules","typescript","warnings"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jens-duttke.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"jens-duttke"}},"created_at":"2019-01-16T09:46:05.000Z","updated_at":"2021-08-26T11:30:08.000Z","dependencies_parsed_at":"2022-08-07T23:31:30.894Z","dependency_job_id":null,"html_url":"https://github.com/jens-duttke/tslint-filter","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jens-duttke%2Ftslint-filter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jens-duttke%2Ftslint-filter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jens-duttke%2Ftslint-filter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jens-duttke%2Ftslint-filter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jens-duttke","download_url":"https://codeload.github.com/jens-duttke/tslint-filter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247838447,"owners_count":21004580,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["errors","exception","extend-rules","failures","filter","ignore","modify","predefined-rule-wrappers","regular-expression","rules","tslint","tslint-filter","tslint-rules","typescript","warnings"],"created_at":"2024-12-22T00:34:27.659Z","updated_at":"2025-04-08T12:16:20.254Z","avatar_url":"https://github.com/jens-duttke.png","language":"TypeScript","funding_links":["https://github.com/sponsors/jens-duttke"],"categories":[],"sub_categories":[],"readme":"[![npm version](https://badge.fury.io/js/tslint-filter.svg)](https://badge.fury.io/js/tslint-filter)\n[![Dependency Status](https://img.shields.io/david/jens-duttke/tslint-filter.svg)](https://www.npmjs.com/package/tslint-filter)\n[![Known Vulnerabilities](https://snyk.io/test/github/jens-duttke/tslint-filter/badge.svg?targetFile=package.json)](https://snyk.io/test/github/jens-duttke/tslint-filter?targetFile=package.json)\n[![npm](https://img.shields.io/npm/dm/tslint-filter.svg?maxAge=2592000)](https://www.npmjs.com/package/tslint-filter)\n[![MIT license](https://img.shields.io/github/license/jens-duttke/tslint-filter.svg?style=flat)](https://opensource.org/licenses/MIT)\n\n# TSLint-Filter\nSuppress and extend TSLint linting errors, before they get returned to the console or your code editor.\n\n**Table of Contents**\n\n- [Use Cases](#use-cases)\n  - [Ignore specific linting errors](#ignore-specific-linting-errors)\n  - [Extend rules](#extend-rules)\n  - [Handle JavaScript errors in rules](#handle-javascript-errors-in-rules)\n- [Installation](#installation)\n- [Predefined rule wrappers](#predefined-rule-wrappers)\n- [Disable/enable rules by their original name in comment flags in source code](#disableenable-rules-by-their-original-name-in-comment-flags-in-source-code)\n- [Custom rule wrappers](#custom-rule-wrappers)\n  - [Basic Usage](#basic-usage)\n  - [Extended Usage](#extended-usage)\n    - [Example #1](#example-1)\n    - [Example #2](#example-2)\n  - [Ranges in regular expressions](#ranges-in-regular-expressions)\n  - [Location of Rule Directories](#location-of-rule-directories)\n  - [Rule File Names](#rule-file-names)\n\n## Use Cases\n\nMany TSLint rules are very limited by their configurability, and some rules looks like they are not thought to the end.\n\n### Ignore specific linting errors\n\nFor example, I want to prevent the usage of \"I\" as prefix for interface names. The TSLint rule for that is called \"interface-name\".\u003cbr /\u003e\nUnfortunately, this rule also shows an error for \"I18N\", which is an absolutely valid interface name to me.\n\nOr, in my React projects I want to get a linting error, if I forgot to specify a Components class method as `private` or `public` using the \"member-access\" rule. But for the React methods `componentDidMount`, `render`, `getDerivedStateFromProps` etc. I don't want to specify that, because they are always public.\u003cbr /\u003e\nUnfortunately, by now, it's not possible to specify a whitelist here.\n\n### Extend rules\n\nI want to prefer conditional expressions for small, simple alignments, but \"prefer-conditional-expression\" also complains about complex statements, which wouldn't be easy readable in a single line, because this line would have a size of 300 characters or more.\u003cbr /\u003e\nWhy isn't there a way to show the linting error only, if the conditional expression would be a ...let's say... less-than-120-chars-one-liner?\n\nUsing TSLint-Filter, you have the possibility to easily extend existing rules and suppress specific linting errors, based on regular expressions.\n\nIt's even possible to use integer [ranges in these regular expression](#ranges-in-regular-expressions), to filter by a range of numbers in the error message.\n\n### Handle JavaScript errors in rules\n\nAt this time (2019-01-17), the [tslint-microsoft-contrib](https://www.npmjs.com/package/tslint-microsoft-contrib) rule \"import-name\" throws an error for empty imports (imports of modules for side effects only) like\n```javascript\nimport './polyfill'\n```\nWhile TSLint doesn't handle such JavaScript errors, your code editor may suppress this error silently and may stop linting your whole project or at least the current file, so that you think your files are free of issues, because your editor doesn't show any.\n\nTSLint-Filter catches such JavaScript errors and show them as normal linting errors for the first character of a file, so that you get visual feedback, that there's something wrong.\n\nUsing the filter ability of TSLint-Filter you are then also able to suppress the specific error, without to affect the execution of other rules.\n\n\u003e Don't forget to report errors to rule authors, so that they are able to fix them!\n\n## Installation\n\nInstall with npm\n\n```sh\nnpm install tslint-filter --save-dev\n```\n\nSince TSLint does not provide an easy way to modify linting errors before they get returned, we need to create own rules, with TSLint-Filter as wrapper for the original rule.\n\nBut that's very easy:\n\nEither use one of the [predefined rule wrappers](#predefined-rule-wrappers), or create a [custom rule wrapper](#custom-rule-wrappers).\n\n## Predefined rule wrappers\n\nThe TSLint-Filter package contains a couple of predefined rule wrappers, which I'm using in my projects.\n\nIn your `tslint.json` either add:\n```\n\"extends\": [\n  \"tslint-filter\"\n]\n```\nor\n```\n\"rulesDirectory\": [\n  \"node_modules/tslint-filter/rules\"\n]\n```\n\nNow, you can replace the original rule, by one of the following predefined rule wrappers:\n\n| Rule Name | Original Rule | Description\n|---|---|---\n| ___deprecation                   | tslint \u0026raquo; [deprecation](https://palantir.github.io/tslint/rules/deprecation) | Adds the context of the deprecated identifier to the message.\u003cbr /\u003e\u003csub\u003e**Original message:**\u003cbr /\u003ewhich is deprecated.\u003cbr /\u003e**New message:**\u003cbr /\u003eKeyboardEvent.which is deprecated.\u003c/sub\u003e\n| ___import-name                   | tslint-microsoft-contrib \u0026raquo; [import-name](https://github.com/Microsoft/tslint-microsoft-contrib#user-content-supported-rules) | Adds the full import path to the message.\u003cbr /\u003e\u003csub\u003e**Original message:**\u003cbr /\u003eMisnamed import. Import should be named 'xyz' but found 'zyx'\u003cbr /\u003e**New message:**\u003cbr /\u003eMisnamed import. Import should be named 'xyz' but found 'zyx' for './my-modules/xyz'\u003c/sub\u003e\n| ___interface-name                | tslint \u0026raquo; [interface-name](https://palantir.github.io/tslint/rules/interface-name) | Adds the criticized interface name to the message.\u003cbr /\u003e\u003csub\u003e**Original message:**\u003e\u003cbr /\u003eInterface name must not have an \"I\" prefix\u003cbr /\u003e**New message:**\u003cbr /\u003eInterface name \"I18N\" must not have an \"I\" prefix\u003c/sub\u003e\n| ___match-default-export-name     | tslint \u0026raquo; [match-default-export-name](https://palantir.github.io/tslint/rules/match-default-export-name) | Adds the full import path to the message.\u003cbr /\u003e\u003csub\u003e**Original message:**\u003cbr /\u003eExpected import 'xyz' to match the default export 'zyx'.\u003cbr /\u003e**New message:**\u003cbr /\u003eExpected import 'xyz' of module './my-modules/xyz' to match the default export 'zyx'.\u003c/sub\u003e\n| ___member-access                 | tslint \u0026raquo; [member-access](https://palantir.github.io/tslint/rules/member-access) | Nothing special. Just enables the ability to filter specific linting errors in the `tslint.json`.\n| ___object-literal-sort-keys      | tslint \u0026raquo; [object-literal-sort-keys](https://palantir.github.io/tslint/rules/object-literal-sort-keys) | Nothing special. Just enables the ability to filter specific linting errors in the `tslint.json`.\n| ___prefer-conditional-expression | tslint \u0026raquo; [prefer-conditional-expression](https://palantir.github.io/tslint/rules/prefer-conditional-expression) | Adds an estimation of the saved characters, and the new size to the message.\u003cbr /\u003e\u003csub\u003e**Original message:**\u003cbr /\u003eUse a conditional expression instead of assigning to 'myVar' in multiple places.\u003cbr /\u003e**New message:**\u003cbr /\u003eUse a conditional expression instead of assigning to 'myVar' in multiple places. (save about 36 characters, conditional expression size would be about 29 characters)\u003c/sub\u003e\n| ___space-in-parens               | tslint-eslint-rules \u0026raquo; [space-in-parens](https://github.com/buzinas/tslint-eslint-rules/blob/master/src/docs/rules/spaceInParensRule.md) | Allow block comments inside of parentheses, like:\u003cbr /\u003e```import(/* webpackChunkName: \"my-chunk-name\" */'module');```\n| ___strict-boolean-expressions    | tslint \u0026raquo; [strict-boolean-expressions](https://palantir.github.io/tslint/rules/strict-boolean-expressions) | Adds the context of the expression to the message.\u003cbr /\u003e\u003csub\u003e**Original message:**\u003cbr /\u003eThis type is not allowed in the operand for the '\u0026\u0026' operator because it is always truthy. It may be null/undefined, but neither 'allow-null-union' nor 'allow-undefined-union' is set. Allowed types are boolean, enum, or boolean-or-undefined.\u003cbr /\u003e**New message:**\u003cbr /\u003eThis type is not allowed in the operand for the '\u0026\u0026' operator in JsxExpression because it is always truthy. It may be null/undefined, but neither 'allow-null-union' nor 'allow-undefined-union' is set. Allowed types are boolean, enum, or boolean-or-undefined.\u003c/sub\u003e\n| ___typedef                       | tslint \u0026raquo; [typedef](https://palantir.github.io/tslint/rules/typedef) | Nothing special. Just enables the ability to filter specific linting errors in the `tslint.json`.\n\nThe configuration is equally to the original rule, expect that the last argument takes an array of regular expression. Like:\n```\n\"___prefer-conditional-expression\": [true, \"check-else-if\", [\n  \"conditional expression size would be about [120...]\"\n]],\n```\n(see the topic [ranges in regular expressions](#ranges-in-regular-expressions) to read about the above regexp)\n\n\u003e Even if you just use TSLint-Filter to modify the linting error message, you need to add an empty array, if the last argument of the rule is already an array. Otherwise, TSLint-Filter will misinterpret the rule argument as array of RegExp pattern to ignore.\n\n## Disable/enable rules by their original name in comment flags in source code\n\nTSLint allows you to enable or disable specific rules by their name directly in the source code, like\n```javascript\n// tslint:disable-next-line:rule-name\n```\nNormally, the `rule-name` is the name of the rule you use in your `tslint.json`. That would mean, if you change `interface-name` to `___interface-name`, you would also need to update all comments which are using this rule name.\n\nTo avoid that, TSLint-Filter pretend to have the name of the original rule, so you don't need to change anything.\n\n## Custom rule wrappers\n\n### Basic Usage\n\nFirst, create a folder for custom rules in your project folder.\n\nIn this folder create a JavaScript file like this:\n```javascript\nmodule.exports = require('tslint-filter')('tslint/lib/rules/memberAccessRule');\n```\n\"tslint/lib/rules/memberAccessRule\" is the name of the original rule, which you want to extend.\n\nYou can name the file to whatever you want, but it must end with \"Rule.js\". I prefer to use the name of the original rule, and prefix it with \"___\" (3x underscore), so in this case \"___memberAccessRule.js\".\n\nIn your `tslint.json` add the folder to the \"rulesDirectory\" section:\n```json\n{\n  \"rulesDirectory\": [\n    \"script/custom-tslint-rules\"\n  ],\n  \"rules\": {\n```\n\nNow, instead of using the rule \"member-access\", you're able to use the rule \"___member-access\".\u003cbr /\u003e\n\nIf the last argument is an array it will be interpreted as an array of regular expressions. Linting errors which match these expressions will be ignored.\n\n```json\n\"___member-access\": [true, [\n  \"'(getDerivedStateFromProps|componentDidMount|shouldComponentUpdate|render|getSnapshotBeforeUpdate|componentDidUpdate|componentWillUnmount)'\"\n]],\n```\n\n### Extended Usage\n\nBeside simply ignoring linting errors, you can also manipulate them. You can change the message, implement a fix or whatever you like.\n\nHere is a simply starting point for own scripts:\n```javascript\nconst utils = require('tsutils');\n\nmodule.exports = require('tslint-filter')('tslint/lib/rules/...', {\n  /**\n   * @param {import('tslint').RuleFailure} [failure]\n   * @param {import('typescript').SourceFile} [sourceFile]\n   * @param {ts.Program | undefined} [program]\n   */\n  modifyFailure (failure, sourceFile, program) {\n    const node = utils.getTokenAtPosition(sourceFile, failure.getStartPosition().getPosition());\n\n    if (program) {\n      const checker = program.getTypeChecker();\n\n      // Work with types here ...\n    }\n\n    if (node.getText() === 'SomeText') {\n      // If no value is returned, the linting error get suppressed\n      return;\n    }\n\n    if (utils.isImportDeclaration(node.parent)) {\n      // If a string is returned, the original linting error message get changed\n      return `${failure.getFailure()} some more text.`;\n    }\n\n    // Keep the original linting error untouched. You could also create a new RuleFailure and return it\n    return failure;\n  }\n});\n```\n\n#### Example #1\n\nFor example, we want to extend the \"interface-name\" rule, to allow the interface name \"I18N\", even if it starts with \"I\".\u003cbr /\u003e\nUnfortunately, the message of this rule does not provide the name of the interface, so first, we have to include the name into the message:\n```javascript\nconst utils = require('tsutils');\n\nmodule.exports = require('tslint-filter')('tslint-microsoft-contrib/importNameRule', {\n  /**\n   * @param {import('tslint').RuleFailure} [failure]\n   * @param {import('typescript').SourceFile} [sourceFile]\n   */\n  modifyFailure (failure, sourceFile) {\n    if (/^Misnamed import\\./.test(failure.getFailure())) {\n      const node = utils.getTokenAtPosition(sourceFile, failure.getStartPosition().getPosition());\n\n      if (utils.isImportDeclaration(node.parent) \u0026\u0026 utils.isLiteralExpression(node.parent.moduleSpecifier)) {\n        return `${failure.getFailure()} for '${node.parent.moduleSpecifier.text}'`;\n      }\n    }\n\n    return failure;\n  }\n});\n```\n\nNow you can ignore interface names, starting with \"I\" followed by a digit:\n```json\n\"___interface-name\": [true, \"never-prefix\", [\n  \"Interface name \\\"I[\\\\d]\"\n]],\n```\n\n#### Example #2\n\nFor example the \"prefer-conditional-expression\" rule could be extended to show the approximated number of characters you could save, and also the approximated size if you write the statement as conditional expression:\n\n```javascript\nconst utils = require('tsutils');\n\nmodule.exports = require('tslint-filter')('tslint/lib/rules/preferConditionalExpressionRule', {\n  /**\n   * @param {import('tslint').RuleFailure} [failure]\n   * @param {import('typescript').SourceFile} [sourceFile]\n   */\n  modifyFailure (failure, sourceFile) {\n    const match = failure.getFailure().match(/'([^\\0]+)'/);\n\n    if (match !== null) {\n      const node = utils.getTokenAtPosition(sourceFile, failure.getStartPosition().getPosition()).parent;\n\n      if (utils.isIfStatement(node)) {\n        const originalSize = (node.end - node.pos);\n\n        const assigneeLength = match[1].length;\n        const expressionLength = node.expression.end - node.expression.pos;\n        const thenStatementLength = node.thenStatement.getText().replace(/^{?[\\s\\n]+|[\\s\\n]+}?$/g, '').length;\n        const elseStatementLength = node.elseStatement.getText().replace(/^{?[\\s\\n]+|[\\s\\n]+}?$/g, '').length;\n\n        // That's only an approximated size, depending on the wrapping characters\n        const newLength = expressionLength + thenStatementLength + elseStatementLength - assigneeLength + 1;\n\n        if (newLength \u003e originalSize) {\n          return;\n        }\n\n        return `${failure.getFailure()} (save about ${originalSize - newLength} characters, conditional expression size would be about ${newLength} characters)`;\n      }\n    }\n\n    return failure;\n  }\n});\n```\nSave this file under the name \"___preferConditionalExpressionRule.js\" in your custom rule folder.\n\nNow you can use this pattern, to prevent linting errors, where the conditional expression size would be 120 characters or more:\n```json\n\"___prefer-conditional-expression\": [true, \"check-else-if\", [\n  \"conditional expression size would be about [120...]\"\n]],\n```\n\n### Ranges in regular expressions\n\nRanges can be specified with:\n\n| RegExp character sets | Meaning\n|---|---\n| `[-5...5]` | Any integer number from -5 to 5\n| `[...100]` | Any integer number from -999999999999999 to 100\n| `[10...]`  | Any integer number from 10 to 999999999999999\n| `[...]`    | Any integer number from -999999999999999 to 999999999999999,\u003cbr /\u003ebut if possible you should prefer `-?\\d+`\n\n`-999999999999999` and `999999999999999` are required, because the expression is converted into a valid RegExp, and here we always need to specify a range.\u003cbr /\u003e\nThese numbers are chosen because they are very near to Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER, but the RegExp representation is still very short.\n\n### Location of Rule Directories\n\n\u003e It's an open to-do to determine the directory and rule file name automatically, based on the `rulesDirectory`, but I haven't found an easy way to do that yet.\n\nSo, for now, it's required to specify the whole path to the rule, instead of just using the rule name.\n\n| Rule Package | Directory | Number of rules\\*\n|---|---|---:\n| [tslint](https://www.npmjs.com/package/tslint)                                                         | tslint/lib/rules/                        | 153\n| [tslint-microsoft-contrib](https://www.npmjs.com/package/tslint-microsoft-contrib)                     | tslint-microsoft-contrib/                | 93\n| [tslint-sonarts](https://www.npmjs.com/package/tslint-sonarts)                                         | tslint-sonarts/lib/rules/                | 71\n| [rxjs-tslint-rules](https://www.npmjs.com/package/rxjs-tslint-rules)                                   | rxjs-tslint-rules/dist/rules/            | 41\n| [tslint-eslint-rules](https://www.npmjs.com/package/tslint-eslint-rules)                               | tslint-eslint-rules/dist/rules/          | 38\n| [tslint-consistent-codestyle](https://www.npmjs.com/package/tslint-consistent-codestyle)               | tslint-consistent-codestyle/rules/       | 19\n| [tslint-config-security](https://www.npmjs.com/package/tslint-config-security)                         | tslint-config-security/dist/rules/       | 16\n| [tslint-immutable](https://www.npmjs.com/package/tslint-immutable)                                     | tslint-immutable/rules/                  | 16\n| [tslint-misc-rules](https://www.npmjs.com/package/tslint-misc-rules)                                   | tslint-misc-rules/rules/                 | 15\n| [tslint-react](https://www.npmjs.com/package/tslint-react)                                             | tslint-react/rules/                      | 15\n| [tslint-clean-code](https://www.npmjs.com/package/tslint-clean-code)                                   | tslint-clean-code/dist/src/              | 12\n| [tslint-stencil](https://www.npmjs.com/package/tslint-stencil)                                         | tslint-stencil/rules/                    | 7\n| [vrsource-tslint-rules](https://www.npmjs.com/package/vrsource-tslint-rules)                           | vrsource-tslint-rules/rules/             | 7\n| [rxjs-tslint](https://www.npmjs.com/package/rxjs-tslint)                                               | rxjs-tslint/                             | 4\n| [tslint-jasmine-rules](https://www.npmjs.com/package/tslint-jasmine-rules)                             | tslint-jasmine-rules/dist/               | 3\n| [tslint-defocus](https://www.npmjs.com/package/tslint-defocus)                                         | tslint-defocus/dist/                     | 1\n| [tslint-lines-between-class-members](https://www.npmjs.com/package/tslint-lines-between-class-members) | tslint-lines-between-class-members/      | 1\n| [tslint-no-unused-expression-chai](https://www.npmjs.com/package/tslint-no-unused-expression-chai)     | tslint-no-unused-expression-chai/rules/  | 1\n| [tslint-origin-ordered-imports-rule](https://www.npmjs.com/package/tslint-origin-ordered-imports-rule) | tslint-origin-ordered-imports-rule/dist/ | 1\n| [tslint-plugin-prettier](https://www.npmjs.com/package/tslint-plugin-prettier)                         | tslint-plugin-prettier/rules/            | 1\n\n\\* as of 2019-03-01. List ordered by number of rules.\n\n### Rule File Names\n\nDashes in the file names are converted to camel-case, but leading and trailing dashes are kept. \"Rule\" is appended.\n\nSo, the rule name `-ab-cd-ef-` is located in the file `-abCdEf-Rule`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjens-duttke%2Ftslint-filter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjens-duttke%2Ftslint-filter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjens-duttke%2Ftslint-filter/lists"}