Opinionated ESLint plugin for TypeScript
npm install --save-dev @sethlivingston/eslint-plugin-typescript-narrowsimport tsnarrows from "@sethlivingston/eslint-plugin-typescript-narrows";
export default [
...tsnarrows.configs.strict,
...tsnarrows.configs.test,
...tsnarrows.configs.tooling,
];Use the presets together in that order:
strictfor ordinary source filestestfor*.test.*,*.spec.*,__tests__/, andtests/helperstoolingfor config entrypoints such aseslint.config.ts,vite.config.ts,vitest.config.ts, andtsup.config.ts
| Preset | Intended files | Notes |
|---|---|---|
configs.strict |
Source code | Main strict preset for production and library code |
configs.test |
Test files and test helpers | Relaxes ceremony-heavy rules like explicit return types and require-await, while keeping safety rules like no-floating-promises |
configs.tooling |
Tooling and config entrypoints | Keeps strict behavior but turns off import/no-default-export for conventional config files |
The strict preset also allows build-time injected constants that follow ^__[_A-Z0-9]+__$, including declared globals and object-literal define maps. Object literal property keys that require quotes (hyphenated headers, digit-leading keys like "4xx", etc.) are exempt from naming-convention format checks — enforcing camelCase on a name that cannot be an identifier is meaningless.
@typescript-eslint/prefer-readonly-parameter-types is intentionally excluded from the strict preset. TypeScript's own readonly field enforcement already covers the meaningful cases; the rule's recursive type-checking generates noise against browser platform classes (URL, Headers, Response, etc.) without delivering proportional safety benefit. Teams that want this level of enforcement can opt in locally.
- 20+ typescript-eslint rules configured to opinionated defaults
- 3 ESLint core rules
- 3 import-x rules
- 2 custom rules (ban-enums, ban-barrel-files)
30+ rules total, enforcing over half of the project's opinions (some rules cover multiple opinions).
| Rule | Description | Auto-fixable |
|---|---|---|
typescript-narrows/ban-enums |
Bans enum and const enum declarations in favor of as const objects |
No |
typescript-narrows/ban-barrel-files |
Bans barrel files (index.ts re-export files) | No |
This plugin pairs with The TypeScript Narrows Claude Plugin, which provides all opinions as structured guidance for AI-assisted development. The skill covers the opinions that cannot be enforced through lint rules.
The ESLint plugin package uses automated release workflows. To publish a release, you must:
- Bump
versionineslint-plugin/package.jsononmain - Have permission to push to the repository
- Have the
npm-publishGitHub environment configured with:- Trusted publishing enabled (OIDC with npm)
- Appropriate npm permissions for
@sethlivingston/eslint-plugin-typescript-narrows
The release is triggered by pushing a git tag with the format eslint-plugin/v{VERSION}, where {VERSION} matches the version field in eslint-plugin/package.json.
To release a new version:
- Update
eslint-plugin/package.jsonwith the new version (e.g.,"version": "1.2.0") - Commit the version bump to
mainand push - Create and push a tag:
git tag eslint-plugin/v1.2.0 && git push origin eslint-plugin/v1.2.0
- CI Workflow (
.github/workflows/ci-eslint-plugin.yml): Runs on pull requests and pushes to main, validating the package builds, passes tests, and typechecks successfully - Release Workflow (
.github/workflows/release-eslint-plugin.yml): Triggered by tags matchingeslint-plugin/v*, performs validation and publishes to npm with provenance, then creates a GitHub release with auto-generated release notes