how to write custom rules for eslint using modern

how to write custom rules for eslint using modern typescript parser is the central topic of this article, and the following content explains the key points, practical uses, and decisions around it.

ESLint has become an indispensable tool for maintaining code quality in JavaScript and TypeScript projects, but out-of-the-box rules may not cover every unique pattern your team needs. That’s where custom rules come in, and knowing how to write custom rules for eslint using modern typescript parser unlocks the ability to enforce project-specific conventions with precision, leveraging TypeScript’s type-aware AST analysis for more accurate checks than plain JavaScript parsers can provide.

Understanding the Modern TypeScript Parser for ESLint

The modern TypeScript parser (part of the @typescript-eslint project) differs from legacy ESLint parsers by generating a type-aware Abstract Syntax Tree (AST). This means every node in the AST carries semantic type information from your TypeScript code, rather than just syntax details. For TypeScript projects, this is a critical upgrade: custom rules can now check not just if a line is written correctly, but if it adheres to type-specific rules, like ensuring a function returns a type you expect or a property is used only in compatible contexts.

What Makes the Modern TS Parser Unique

Unlike older parsers that treat TypeScript as a superset of JavaScript without deeply understanding its constructs, the modern parser natively handles interfaces, generics, type assertions, and module declarations. It integrates directly with the TypeScript compiler API, so it pulls exact type information from your project’s tsconfig.json, eliminating guesswork in rule logic. This reduces false positives, as rules can rely on verified type data rather than parsing code in isolation.

Setting Up Your Rule Development Environment

To write custom rules with the modern parser, you’ll need a dedicated environment with TypeScript and ESLint’s TypeScript utilities. Start by creating a new npm package for your plugin, then install key dependencies: eslint, @typescript-eslint/parser, @typescript-eslint/rule-tester, and @typescript-eslint/utils. These tools simplify rule creation, testing, and integration, ensuring compatibility with the latest ESLint and TypeScript versions.

  • Initialize your plugin project with `npm init --yes` and name it like `eslint-plugin-custom-ts-rules` to follow ESLint naming conventions.
  • Install dependencies via `npm install eslint @typescript-eslint/parser @typescript-eslint/rule-tester @typescript-eslint/utils --save-dev`.
  • Configure your tsconfig.json to target ES2020, set `module` to CommonJS, and exclude test files if needed, matching ESLint’s requirements.
  • Add a `build` script to compile your TypeScript rules to JavaScript, as ESLint expects plugins to run on compiled code.

Anatomy of a Type-Aware Custom ESLint Rule

Every custom ESLint rule built with the modern TypeScript parser follows a standard structure, with three core components: a metadata object, a create function, and visitor methods. The metadata defines the rule’s name, description, severity, and whether it’s type-aware—critical for ESLint to apply it correctly. The create function receives an ESLint context object, which gives access to the AST, TypeScript’s type checker, and the method to report errors. Visitor methods are functions that run when ESLint traverses a specific node in the AST, like FunctionDeclaration or PropertyAccessExpression.

Leveraging Type Utilities for Precise Checks

The @typescript-eslint/utils package provides type guards and helpers to work with TypeScript-specific AST nodes. For example, isTSFunctionType lets you confirm a node is a TypeScript function type, while getTypeServices gives you access to the project’s type checker. These utilities let you write rules that don’t just look at syntax text, but actual type information—like checking if a variable is of type `User` before allowing a method call on it, a check impossible with non-type-aware parsers.

Writing a Practical Custom Rule: Enforce Explicit Void Returns

Let’s build a real-world rule: ensuring TypeScript functions explicitly declare `void` as their return type when they don’t return a value. This prevents accidental implicit `any` returns and makes code intent clearer. The rule will use the type checker to verify the inferred return type, so it works across arrow functions, async functions, and regular functions.

Start by importing necessary utilities: `AST_NODE_TYPES` from eslint, `isType`, `getTypeServices`, and `isArrowFunctionExpression` from @typescript-eslint/utils. The create function will use the type checker to get the return type of each function node, then report an error if the return type is not explicitly `void` or `undefined` for functions that don’t return a value.

Testing Your Rule with ESLint’s Rule Tester

Before using your rule in production, test it with @typescript-eslint/rule-tester to validate both valid and invalid code cases. For our void return rule, test cases include a function with explicit `void` returns (marked valid), a function without a return statement that infers a non-void type (marked invalid), and an async function with an implicit return (marked invalid). The rule tester automates this, ensuring your rule behaves correctly across different TypeScript syntax variations.

Integrating Custom Rules into Your Project

Once your rule is tested and compiled, you can add it to any TypeScript project that uses ESLint. If your plugin is published to npm, install it in your project and add it to your .eslintrc.json under the `plugins` array. Then enable your rule by name, setting its severity level (like `error` to enforce it strictly). If your plugin is local, reference it directly in the config using the `plugin:` prefix.

Ensure your project’s ESLint config uses @typescript-eslint/parser as its parser, so the modern TypeScript AST is available to your custom rules. This step is non-negotiable—without the correct parser, your rules will not have access to the type information they rely on.

Writing custom ESLint rules with the modern TypeScript parser is a powerful way to tailor code quality to your team’s specific needs, going beyond generic rules to enforce type-aware, project-specific conventions. By setting up a proper development environment, leveraging TypeScript’s type utilities, testing rigorously, and integrating correctly, you can build rules that reduce bugs, improve maintainability, and align code with your standards. The modern parser’s type-aware analysis turns custom rules from simple syntax checks into valuable tools that understand the full context of your TypeScript codebase.

All content about “how to write custom rules for eslint using modern typescript parser” is originally written by our expert editorial team. Reproduction without permission is strictly prohibited.