Custom Rules

TSLint ships with a set of core rules that can be configured. However, users are also enabled to write their own rules, which allows them to enforce specific behavior not covered by the core of TSLint. TSLint’s internal rules are itself written to be pluggable, so adding a new rule is as simple as creating a new rule file named by convention. New rules can be written in either TypeScript or Javascript; if written in TypeScript, the code must be compiled to Javascript before registering them with TSLint.

Important conventions: Rule identifiers are always kebab-cased. Their implementation files are always camelCasedRule.ts and must contain the suffix Rule.

Let us take the example of how to write a new rule to forbid all import statements (you know, for science). Let us name the rule file noImportsRule.ts. Rules are referenced in tslint.json with their kebab-cased identifer, so "no-imports": true would configure the rule.

Now, let us first write the rule in TypeScript. A few things to note:

  • We import tslint/lib/lint to get the whole Lint namespace instead of just the Linter class.
  • The exported class must always be named Rule and extend from Lint.Rules.AbstractRule.

```ts import * as ts from “typescript”; import * as Lint from “tslint/lib/lint”;

export class Rule extends Lint.Rules.AbstractRule { public static FAILURE_STRING = “import statement forbidden”;

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

// The walker takes care of all the work. class NoImportsWalker extends Lint.RuleWalker { public visitImportDeclaration(node: ts.ImportDeclaration) { // create a failure at the current position this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING));

    // call the base version of this visitor to actually parse this node
    super.visitImportDeclaration(node);
} } ```

Given a walker, TypeScript’s parser visits the AST using the visitor pattern. So the rule walkers only need to override the appropriate visitor methods to enforce its checks. For reference, the base walker can be found in syntaxWalker.ts.

We still need to hook up this new rule to TSLint. First make sure to compile noImportsRule.ts:

bash tsc --noImplicitAny noImportsRule.ts

Then, if using the CLI, provide the directory that contains this rule as an option to --rules-dir. If using TSLint as a library or via grunt-tslint, the options hash must contain "rulesDirectory": "...". If you run the linter, you’ll see that we have now successfully banned all import statements via TSLint!

Finally, add a line to your tslint.json config file for each of your custom rules.

Final notes:

  • Core rules cannot be overwritten with a custom implementation.
  • Custom rules can also take in options just like core rules (retrieved via this.getOptions()).