Frontend Development 12 min read

Creating Custom ESLint Plugins to Enforce Team Coding Standards

This article explains how to develop custom ESLint plugins—covering background concepts, Yeoman generator setup, rule implementation for typeof and instanceof checks, unit testing, local linking, and providing recommended configurations—to ensure consistent code style and best‑practice enforcement across a development team.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Creating Custom ESLint Plugins to Enforce Team Coding Standards

In many projects multiple developers work on the same codebase, making it hard to keep everyone aware of best‑practice guidelines, especially when team members change. Traditional code review is delayed and relies on manual inspection, which cannot guarantee 100% quality.

Using an intelligent assistant that can suggest library functions in real time solves this problem; for front‑end JavaScript development, ESLint serves that role. This guide shows how to extend ESLint with custom plugins to enforce project‑specific best practices.

Background Knowledge

The article first recalls a previous discussion about the pitfalls of using typeof and instanceof for type checking in JavaScript, illustrating the issues with a short code snippet.

typeof []; // 'object'
typeof {}; // 'object' typeof c; // 'object'

[] instanceof Array // true
[] instanceof Object // true  // note this

Two custom ESLint rules will be created to detect these patterns.

Plugin Creation

ESLint plugins are generated with the Yeoman generator. Install Yeoman and the ESLint generator globally:

$ npm i -g yo
$ npm i -g generator-eslint

Create a new directory for the plugin and run the generator:

$ mkdir eslint-plugin-utils
$ cd eslint-plugin-utils
$ yo eslint:plugin

The generator produces a project structure where lib/rules holds custom rules and tests/lib/rules holds their unit tests.

.
├── .eslintrc.js
├── README.md
├── lib
│   ├── index.js
│   └── rules
├── package-lock.json
├── package.json
└── tests
    └── lib
        └── rules

type-typeof-limit Rule

This rule flags any expression where typeof is compared to the string 'object' . The rule metadata must set type to problem and provide a description and category (e.g., Best Practices ).

module.exports = {
  meta: {
    type: null, // change to 'problem'
    docs: {
      description: 'typeof cannot be used for objects or arrays, use @jsmini/type',
      category: 'Fill me in', // change to 'Best Practices'
      recommended: false,
      url: null,
    },
    fixable: null,
    schema: [],
  },
  create(context) {
    return {};
  },
};

The core logic inspects BinaryExpression nodes, checks that the left side is a UnaryExpression with operator typeof , the operator is == or === , and the right side is a literal 'object' . If all conditions match, context.report emits an error.

module.exports = {
  create(context) {
    return {
      BinaryExpression: (node) => {
        const operator = node.operator;
        const left = node.left;
        const right = node.right;
        if (
          (operator === '==' || operator === '===') &&
          left.type === 'UnaryExpression' &&
          left.operator === 'typeof' &&
          right.type === 'Literal' &&
          right.value === 'object'
        ) {
          context.report({
            node,
            message: 'typeof不能用于对象和数组,请使用 @jsmini/type',
          });
        }
      },
    };
  },
};

Unit tests are written with ESLint's RuleTester . The test file defines one valid case and two invalid cases that should trigger the rule.

const rule = require('../../../lib/rules/type-typeof-limit'),
      RuleTester = require('eslint').RuleTester;

const msg = 'typeof不能用于对象和数组,请使用@jsmini/type';

const ruleTester = new RuleTester();
ruleTester.run('type-typeof-limit', rule, {
  valid: [{ code: 'typeof a == "number"' }, { code: 'a == "object"' }],
  invalid: [
    { code: 'typeof a == "object"', errors: [{ message: msg }] },
    { code: 'typeof a === "object"', errors: [{ message: msg }] },
  ],
});

Run the tests with npm test to verify the rule works.

type-instanceof-limit Rule

A second rule detects the use of instanceof Object , which may also be problematic. Its implementation follows the same pattern, checking for a BinaryExpression whose operator is instanceof and reporting an error.

module.exports = {
  create(context) {
    function check(node) {
      const operator = node.operator;
      if (operator === 'instanceof') {
        context.report({
          node,
          message: 'instanceof操作符可能存在问题,请使用@jsmini/type',
        });
      }
    }
    return { BinaryExpression: check };
  },
};

Recommended Configuration

With multiple rules, a shared configuration simplifies usage. Add a configs section to lib/index.js that exports a recommended preset.

module.exports = {
  rules: requireIndex(__dirname + '/rules'),
  configs: {
    plugins: ['@jsmini/utils'],
    rules: {
      '@jsmini/utils/type-typeof-limit': 'error',
      '@jsmini/utils/type-instanceof-limit': 'error',
    },
  },
};

Consumers can then enable the preset with:

module.exports = {
  extends: ['@jsmini/utils:recommended'],
};

Local Linking for Development

During development, link the plugin locally:

$ npm link
$ cd ../eslint-plugin-utils-demo
$ npm link @jsmini/eslint-plugin-utils

Configure the demo project's .eslintrc.js to enable the custom rules, write a test file containing typeof a === 'object' , and verify that ESLint highlights the error.

Conclusion

The article demonstrates how to create two custom ESLint rules— type-typeof-limit and type-instanceof-limit —and integrate them into a project with unit tests and a recommended configuration, thereby ensuring consistent code style, reducing review overhead, and improving overall code quality.

JavaScriptfrontend developmentcode qualityStatic AnalysisESLintCustom Plugin
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.