77 opinionated ESLint rules for TypeScript teams that refuse to compromise on code quality.
Zero tolerance means every rule earns its place. No warnings you learn to ignore. No exceptions you forget about. Every violation is a conversation about quality โ and quality always wins.
Most linting setups start strict and erode over time. A scattered eslint-disable here, an any cast there, and before long the rules exist in name only.
This plugin takes the opposite approach:
- No
eslint-disablecomments โ fix the root cause, don't silence the symptom. - No
anysmuggling โ type assertions and non-null assertions are flagged. - No magic values โ every number and string earns a name.
- No leaky tests โ persistent mocks, imprecise matchers, and timer abuse are caught.
- No complexity hiding โ functions stay short, parameters stay few, imports stay clean.
The result is a codebase where the rules are the culture and the culture is visible in every file.
| Hosted docs | https://coderrob.github.io/eslint-config-zero-tolerance/ |
| Rules reference | docs/rules/index.md |
| Configuration guide | docs/configuration.md |
This monorepo publishes one package:
| Package | Description |
|---|---|
@coderrob/eslint-plugin-zero-tolerance |
The ESLint plugin โ 77 custom rules |
packages/config is retained as an internal workspace package for development and compatibility testing; it is not published to npm.
- ESLint 8.57.0+, 9.x, or 10.x
- TypeScript-ESLint 8.x
- TypeScript 5.x
npm install --save-dev @coderrob/eslint-plugin-zero-tolerance @typescript-eslint/parserUsing the recommended preset:
// eslint.config.js
import zeroTolerance from '@coderrob/eslint-plugin-zero-tolerance';
export default [
zeroTolerance.configs.recommended,
// your other configs...
];Using the strict preset:
// eslint.config.js
import zeroTolerance from '@coderrob/eslint-plugin-zero-tolerance';
export default [
zeroTolerance.configs.strict,
// your other configs...
];Custom configuration:
// eslint.config.js
import zeroTolerance from '@coderrob/eslint-plugin-zero-tolerance';
export default [
{
plugins: {
'zero-tolerance': zeroTolerance,
},
rules: {
'zero-tolerance/require-interface-prefix': 'error',
'zero-tolerance/no-throw-literal': 'error',
'zero-tolerance/max-function-lines': ['warn', { max: 40 }],
// ... other rules
},
},
];Using .eslintrc.js:
module.exports = {
plugins: ['@coderrob/zero-tolerance'],
extends: ['plugin:@coderrob/zero-tolerance/legacy-recommended'],
// or for strict mode:
// extends: ['plugin:@coderrob/zero-tolerance/legacy-strict'],
};The plugin ships 77 rules across 8 categories. The grouped catalog below is exhaustive and links every rule to its dedicated documentation page.
Preset columns report the configured severity: ๐ผ for error, warn, and ๐ซ for off. A blank cell means the preset does not configure the rule.
| Category | Rules | Focus |
|---|---|---|
| Naming Conventions | 1 | Interface naming standards |
| Documentation | 5 | JSDoc, BDD specs, optional chaining, readonly props |
| Testing | 8 | Test descriptions, mocks, timers, fetch, interfaces |
| Type Safety | 12 | Assertions, unions, imports, exported types |
| Code Quality | 16 | Function size, magic values, immutability, sorting |
| Error Handling | 3 | Throw safety, empty catches, Result patterns |
| Imports | 12 | Barrels, re-exports, dynamic imports, node protocol |
| Bug Prevention | 20 | Identical code, control flow, async safety |
๐๏ธ The type of rule.
โ Identifies problems that could cause errors or unexpected behavior.
๐ Identifies potential improvements.
๐ผ Configurations enabled in.
๐ซ Configurations disabled in.
R Set in the recommended configuration.
S Set in the strict configuration.
Interface naming standards
| Name | ๐๏ธ | ๐ผ | ๐ซ | Description | |
|---|---|---|---|---|---|
| require-interface-prefix | ๐ | S | R | Enforce that interface names start with "I" |
JSDoc, BDD specs, optional chaining, readonly props
| Nameย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย | ๐๏ธ | ๐ผ | ๐ซ | Description | |
|---|---|---|---|---|---|
| require-bdd-spec | ๐ | R S | Enforce sibling, source-reference, and export relationships for BDD specs | ||
| require-jsdoc-anonymous-functions | ๐ | R S | Require JSDoc comments on anonymous function-like constructs except in test files and known test callbacks | ||
| require-jsdoc-functions | ๐ | S | R | Require JSDoc comments on all functions and require @param/@returns/@throws tags when applicable (except in test files) | |
| require-optional-chaining | ๐ | S | R | Require optional chaining instead of repeated logical guard access | |
| require-readonly-props | ๐ | S | R | Require readonly typing for JSX component props |
Test descriptions, mocks, timers, fetch, interfaces
| Nameย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย | ๐๏ธ | ๐ผ | ๐ซ | Description | |
|---|---|---|---|---|---|
| no-fetch-in-tests | ๐ | R S | Disallow fetch usage in test files | ||
| no-jest-have-been-called | ๐ | S | R | Prohibit toBeCalled, toHaveBeenCalled, toBeCalledWith, toHaveBeenCalledWith, toHaveBeenLastCalledWith, and toLastCalledWith; use toHaveBeenCalledTimes with an explicit call count and toHaveBeenNthCalledWith with an explicit nth-call index and arguments instead | |
| no-mock-implementation | ๐ | S | R | Prohibit persistent mock implementations; use the Once variants to avoid test bleeds | |
| no-restricted-imports-in-tests | ๐ | R S | Disallow restricted dependency imports in test files | ||
| no-set-interval-in-tests | ๐ | S | R | Disallow setInterval usage in test files | |
| no-set-timeout-in-tests | ๐ | S | R | Disallow setTimeout usage in test files | |
| no-test-interface-declaration | ๐ | S | R | Disallow interface declarations in test files; import production types instead | |
| require-test-description-style | ๐ | S | R | Enforce that test descriptions start with "should" |
Assertions, unions, imports, exported types
| Nameย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย | ๐๏ธ | ๐ผ | ๐ซ | Description | |
|---|---|---|---|---|---|
| no-destructured-parameter-type-literal | ๐ | S | R | Disallow inline object type literals on destructured parameters; require a named type instead | |
| no-explicit-any | โ | S | R | Disallow explicit any; model unknown values precisely and narrow them explicitly | |
| no-indexed-access-types | โ | S | R | Disallow TypeScript indexed access types | |
| no-inline-type-import | โ | S | R | Disallow TypeScript inline type imports using import("...") | |
| no-literal-property-unions | ๐ | S | R | Require property literal unions to use named domain types | |
| no-literal-unions | ๐ | S | R | Ban literal unions in favor of enums | |
| no-non-null-assertion | โ | S | R | Disallow non-null assertions using the "!" postfix operator | |
| no-return-type | โ | S | R | Disallow TypeScript ReturnType utility usage | |
| no-type-assertion | ๐ | S | R | Prevent use of TypeScript "as" type assertions | |
| no-unsafe-json-parse | โ | S | R | Disallow treating JSON.parse results as typed data without validation | |
| require-exported-object-type | ๐ | S | R | Require exported object constants to declare an explicit type annotation | |
| require-union-type-alias | ๐ | S | R | Require inline union types with three or more members and multiple type references to be extracted into named type aliases |
Function size, magic values, immutability, sorting
| Nameย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย | ๐๏ธ | ๐ผ | ๐ซ | Description | |
|---|---|---|---|---|---|
| max-function-lines | ๐ | S | R | Enforce a maximum number of lines per function body | |
| max-params | ๐ | S | R | Enforce a maximum number of function parameters | |
| no-array-mutation | ๐ | S | R | Disallow mutating array methods; prefer immutable alternatives such as spread, slice, and toSorted | |
| no-date-now | ๐ | S | R | Disallow Date.now() and new Date(); prefer injected clocks for deterministic behavior | |
| no-magic-numbers | ๐ | S | R | Disallow magic numbers; use named constants instead of raw numeric literals | |
| no-magic-strings | ๐ | S | R | Disallow magic strings in comparisons and switch cases; use named constants instead | |
| no-map-set-mutation | ๐ | S | R | Disallow direct Map and Set mutation methods; rebuild collections instead of mutating them in place | |
| no-object-mutation | ๐ | S | R | Disallow direct object-property mutation; prefer creating new objects with immutable update patterns | |
| no-placeholder-implementation | โ | S | R | Disallow placeholder, stub, TODO, and not implemented production code | |
| prefer-nullish-coalescing | ๐ | S | R | Prefer nullish coalescing instead of a nullish guard ternary | |
| prefer-object-spread | ๐ | S | R | Enforce object spread syntax instead of Object.assign with an empty object literal as the first argument | |
| prefer-readonly-parameters | ๐ | S | R | Prefer readonly typing for object and array-like parameters to prevent accidental mutation of inputs | |
| prefer-string-raw | ๐ | S | R | Prefer String.raw for string literals containing escaped backslashes (Sonar S7780) | |
| prefer-structured-clone | ๐ | S | R | Prefer structuredClone(...) over JSON.parse(JSON.stringify(...)) when creating a deep clone | |
| sort-functions | ๐ | S | R | Require top-level functions to be sorted alphabetically | |
| sort-imports | ๐ | S | R | Require import declarations to be grouped (side-effect -> builtin -> external -> parent -> peer -> index) and sorted alphabetically within each group |
Throw safety, empty catches, Result patterns
| Nameย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย | ๐๏ธ | ๐ผ | ๐ซ | Description | |
|---|---|---|---|---|---|
| no-empty-catch | โ | S | R | Disallow empty catch blocks that silently swallow errors | |
| no-throw-literal | โ | S | R | Disallow throwing literals, objects, or templates; always throw a new Error instance | |
| prefer-result-return | ๐ | S | R | Prefer Result-style return values instead of throw statements to make error flows explicit and composable |
Barrels, re-exports, dynamic imports, node protocol
| Nameย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย | ๐๏ธ | ๐ผ | ๐ซ | Description | |
|---|---|---|---|---|---|
| no-barrel-parent-imports | ๐ | S | R | Disallow parent-directory imports (.. and ../*) inside barrel files (index.*) across import declarations, import expressions, require calls, and import-equals declarations |
|
| no-dynamic-import | โ | S | R | Ban await import() and require() except in test files | |
| no-export-alias | ๐ | S | R | Prevent use of alias in export statements | |
| no-hardcoded-secrets | โ | S | R | Disallow hardcoded secrets, credentials, tokens, and secret env defaults | |
| no-parent-internal-access | ๐ | R S | Disallow parent-relative access into protected internal directories such as src | ||
| no-raw-sql-interpolation | โ | S | R | Disallow interpolated raw SQL and unsafe raw query helpers | |
| no-re-export | ๐ | S | R | Disallow direct or indirect re-export statements from parent or ancestor modules; barrel files (index.*) are exempt from this restriction | |
| no-shell-command-construction | โ | S | R | Disallow shell command construction through subprocess APIs | |
| no-unsafe-code-generation | โ | S | R | Disallow eval, Function constructors, string timers, and vm code execution APIs | |
| require-barrel-relative-exports | ๐ | S | R | Require barrel re-export declarations to use current-directory descendant paths that start with './' | |
| require-clean-barrel | ๐ | S | R | Require barrel files (index.*) to contain only module re-export declarations | |
| require-node-protocol | ๐ | S | R | Require Node.js built-in module imports to use the node: protocol prefix |
Identical code, control flow, async safety
| Nameย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย | ๐๏ธ | ๐ผ | ๐ซ | Description | |
|---|---|---|---|---|---|
| no-await-in-loop | โ | S | R | Disallow await expressions inside loops; use Promise.all() for parallel execution | |
| no-boolean-return-trap | ๐ | S | R | Disallow ambiguous boolean-return APIs; prefer predicate naming or richer result types for clearer call sites | |
| no-eslint-disable | ๐ | S | R | Prevent use of eslint-disable comments | |
| no-flag-argument | ๐ | S | R | Disallow boolean flag arguments in function declarations; prefer explicit methods or command objects | |
| no-floating-promises | โ | S | R | Disallow floating promises; explicitly handle with await, void, or rejection handlers | |
| no-for-in | โ | S | R | Disallow for..in loops; use Object.keys/values/entries to avoid prototype-chain iteration | |
| no-identical-branches | ๐ | S | R | Disallow identical if/else and conditional-expression branches; consolidate duplicate conditional fragments | |
| no-identical-expressions | โ | S | R | Disallow identical expressions on both sides of a binary or logical operator (Sonar S1764) | |
| no-labels | โ | S | R | Disallow labels because they make control flow harder to reason about | |
| no-math-random | โ | S | R | Disallow Math.random(); inject randomness explicitly or use a dedicated random source | |
| no-parameter-reassign | ๐ | S | R | Disallow assignments and updates to function parameters; use a new local variable instead | |
| no-process-env-outside-config | โ | S | R | Disallow process.env reads outside configuration modules; import typed config instead | |
| no-query-side-effects | ๐ | S | R | Disallow side effects in query-style functions (get*/is*/has*/can*/should*); separate query from modifier | |
| no-redundant-boolean | ๐ | S | R | Disallow redundant comparisons to boolean literals (Sonar S1125) | |
| no-ts-nocheck | โ | S | R | Prevent use of @ts-nocheck comments | |
| no-with | โ | S | R | Disallow with statements because they make scope resolution unpredictable | |
| prefer-guard-clauses | ๐ | S | R | Prefer guard clauses by disallowing else blocks when the if branch already terminates control flow | |
| prefer-shortcut-return | ๐ | S | R | Prefer shortcut boolean returns by replacing if/return true-false patterns with direct return expressions | |
| require-exhaustive-switch | ๐ | S | R | Require exhaustive switch statements over finite union, enum, and boolean discriminants | |
| require-timeout-for-io | โ | S | R | Require timeout or cancellation options for external IO calls |
This repository itself is a
pnpmworkspace and dogfoods its own rules througheslint.config.mjs. Every source file in the plugin must pass the same rules it enforces on consumers.
pnpm installpnpm buildpnpm testCoverage gates are enforced per-file at 95% minimum across statements, branches, functions, and lines. The static badge reports this enforced contract rather than a generated point-in-time percentage.
pnpm readme:sync
pnpm validate:readmeeslint-doc-generator builds the root rule catalog from the published plugin shape and deterministic category metadata in scripts/metadata/readme-rule-catalog.json. It verifies rule documentation links and derives error, warning, and off states from the exported presets.
pnpm --filter @coderrob/eslint-plugin-zero-tolerance exec tsc -p tsconfig.json --noEmit
pnpm --filter @coderrob/eslint-config-zero-tolerance exec tsc -p tsconfig.json --noEmitpnpm deps:graph
pnpm deps:circularOnly @coderrob/eslint-plugin-zero-tolerance is published. Update its version and the dated changelog entry in the release PR, then run the full validation suite. After that commit has passed CI, publish from a clean, authenticated checkout:
pnpm lint
pnpm test
pnpm release:publish:pluginThe publish command targets only packages/plugin; its prepack lifecycle rebuilds the package before pnpm publish --access public. The internal config workspace package is not published.
Apache 2.0 Copyright Robert Lindley