Extending TSLint with Custom Rules

Learn how to extend your TSLint rules with your own custom rules

--

TSLint is an extensible static analysis tool that checks Javascript and TypeScript code for readability, maintainability, and functionality errors. It can be integrated into build systems and editors. It has a set of core rules built into and configuration that allows it to be extended with custom rules.

Many libraries have leveraged this configuration to build massive and wonderful tools out of it. The popular among these is the Codelyzer, built by Minko Gechev. The Codelyzer has custom rules that provide a way to lint Angular/TypeScript projects. The library is amazing, yes, and we too can do the same. Here, we will learn how to add our custom rules to the tslint.

Tip: Another great way to ensure maintainability and to test for functionality errors is by sharing and reusing components using Bit. Bit gives you the power to “harvest” components from any codebase and share them to a component collection in bit.dev. It’s also a great way to speed up development and optimize team collaboration.

Example: browsing through shared components in bit.dev

The Tool

First, we create an empty project:

mkdir tslint-ext
cd tslint-ext
npm init -y

Now, we install the dependencies:

npm i typescript tslint tsutils jest ts-jest @types/jest @types/node

The "main" file in our package.json will be pointing to "index.js" therefore, we create index.js file:

touch index.js

Open the file and add:

// ./index.jsmodule.exports = {
rulesDirectory: './',
}

We are export the literal object as the default export. Inside it, we have a "rulesDirectory" property with "./". This would tell tslint that the custom lint rules are in the "./" folder relative to this file.

We will write a custom rule that will enforce pascal-cased class names and interface names.

PascalCase is a naming convention in which the first letter of each word is capitalized. We often use pascal case when writing function names, class names, interface names and other objects. PascalCase is very helpful in distinguishing words within names and helps devs write readable code.

We want users to configure our PascalCased class names by adding “class-name-pascal-case” to tslint.json

To begin, let us create a file that will implement the PascalCase rule:

touch classNamePascalCaseRule.ts

There are important conventions we have to know:

  • Rule identifiers are always kebab-cased.
  • Rule files are always camel-cased.
  • Rule files must contain the suffix Rule.
  • The exported class must always be named Rule.
  • The exported class must extend Lint.Rules.AbstractRule.

Our rule identifier is kebab-cased, “class-name-pascal-case”, so its Rule file must be named “classNamePascalCaseRule.ts” camel-cased with “Rule” suffixed, “classNamePascalCaseRule.ts”.

Open the “classNamePascalCaseRule.ts” file and paste the following:

We need to hook up to tslint. first, we compile it using tsc:

tsc classNamePascalCaseRule.ts

This will generate classNamePascalCaseRule.js in the same dir as classNamePascalCaseRule.ts.

Now, to hook up our custom rule in our project. We can publish this tslint-ext to NPM and import it as a dependency in any project we want to use the custom rule.

Usage from NPM

Let’s publish the tslint-ext to NPM:

npm publish

Note: You have to be logged in NPM, and also push tslint-ext to Github before publishing.

Now, we can install it via:

npm i tslint-ext -D

The -D flag means to install it as a dev dependency.

Let’s say we have a project ts-prj, and we want to use the class-name-pascal-case in it.

ts-prj
src
...
node_modules
tslint.json
package.json
tsconfig.json

First, we install the tslint-ext module.

npm i tslint-ext -D

then we open the tslint.json file in the ts-prj project

{
"extends": "tslint:recommended",
"rules": {
"class-name-pascal-case": true
},
"rulesDirectory": [
"tslint-ext"
]
}

The two important settings here is that we specify the name of the dependency library in the “rulesDirectory” array property, then we specify the rule name in the library we want to use in the “rules” property.

Now, tslint will use the “class-name-pascal-case” when we lint the fields in the ts-prj folder.

If we have a class in our project:

// ts-prj/component.tsexport class BARCHART_COMPONENT {}

We have a class BARCHART_COMPONENT here. The name here is not pascal-cased, so when we run tslint on it we will see the error "Class name must be in pascal case" on our terminal.

Test

$ tslint -c tslint.json component.tsError at component.ts 3: Class name must be in pascal case

That’s it, our rule is run.

Usage from folder

We can use the rule from the tslint-ext folder without pushing and installing it from NPM.

We can create the tslint-ext inside the ts-prj:

ts-prj
tslint-ext/
...
src/
...
node_modules
tslint.json
package.json
tsconfig.json

Now, the tslint.json will be this

{
"extends": "tslint:recommended",
"rules": {
"class-name-pascal-case": true
},
"rulesDirectory": [
"tslint-ext"
]
}

If the tslint-ext project is inside ts-prj/src folder, then the tslint.json will be this:

{
"extends": "tslint:recommended",
"rules": {
"class-name-pascal-case": true
},
"rulesDirectory": [
"ts-prj/src/tslint-ext"
]
}

Running tslint on component.ts will still succeed:

$ tslint -c tslint.json component.tsError at component.ts 3: Class name must be in pascal case

Conclusion

Adding custom rules to tslint is super easy. It just takes a few very careful configurations to get it right. Now, we know how popular custom linting libraries like codelyzer works.

If you have any questions regarding this or anything I should add, correct or remove, feel free to comment, email or DM me.

Thanks !!!

More TSLint:

Learn More

--

--

JS | Blockchain dev | Author of “Understanding JavaScript” and “Array Methods in JavaScript” - https://app.gumroad.com/chidumennamdi 📕