[eslint-config] Separate type-aware rules, add without-type-information helper, and convert to TypeScript - #6026
Merged
Ian Clanton-Thuon (iclanton) merged 16 commits intoSep 14, 2026
Conversation
…ation helper Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Author the ESLint configuration modules in TypeScript under src/ and build them with Heft (decoupled-local-node-rig) to lib-commonjs. Preserve the existing subpath entry points (e.g. @rushstack/eslint-config/flat/profile/node-trusted-tool) via a package.json "exports" map, and keep CommonJS output using `export =` so that existing require()-based consumers are unaffected. The compiled configuration output is verified to be runtime-identical to the previous JavaScript sources. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… type system Use `satisfies Linter.RulesRecord` on typeAwareRules so its literal rule names are preserved in the inferred type, and type disabledTypeAwareRules as `Record<keyof typeof typeAwareRules, 'off'>`. Adding a type-aware rule now becomes a compile error until it is also disabled in without-type-information. Also add the change file for the TypeScript conversion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… rule group Replace the remaining `import x = require(...)` forms with ES `import` statements (esModuleInterop is enabled), and use `as const satisfies Linter.RulesRecord` for the typeAwareRules group. The compiled configuration remains runtime-identical. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…p the coercion helper typescript-eslint's `RuleModule` type is intentionally not structurally assignable to ESLint's `RuleDefinition`, so a plugin object authored with typescript-eslint's types cannot be assigned to an `ESLint.Plugin` without a widening cast. Move that cast to each @rushstack plugin's export (`@rushstack/eslint-plugin`, `-security`, `-packlets`) so their public type is `ESLint.Plugin`, and drop the `toEslintPlugin` helper from the config. Only the genuinely third-party @typescript-eslint plugin now needs a localized cast. The casts widen through `object` (never `unknown`), and the compiled configuration remains runtime-identical. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…json Explain that the override is needed so that TypeScript uses Node16 module resolution (which honors dependencies' "exports" maps) instead of the legacy "node" resolution implied by the rig's "module": "commonjs", while still emitting CommonJS output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ian Clanton-Thuon (iclanton)
enabled auto-merge (squash)
September 14, 2026 18:30
Ian Clanton-Thuon (iclanton)
force-pushed
the
feature/eslint-config-typescript
branch
from
September 14, 2026 18:30
c3914fe to
6b13230
Compare
Ian Clanton-Thuon (iclanton)
force-pushed
the
feature/eslint-config-typescript
branch
from
September 14, 2026 20:02
a7dcbe7 to
8daeca6
Compare
…eScript
`@typescript-eslint/parser` marks its CommonJS export with `__esModule` but
exposes the parser API on `module.exports` with no `default` export, so a default
`import` resolved to `undefined` at runtime. ESLint then silently fell back to its
built-in parser and failed to parse TypeScript ("Unexpected token ...") across every
consumer of the shared config. Use a namespace import to bind the module export.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bharat Middha (bmiddha)
approved these changes
Sep 14, 2026
Ian Clanton-Thuon (iclanton)
deleted the
feature/eslint-config-typescript
branch
September 14, 2026 21:23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Precursor to #6006. This PR does two related things to
@rushstack/eslint-config(plus small type-only changes to the@rushstackESLint plugins), so that the follow-up work in #6006 can consume a published version:flat/without-type-informationhelper, so TypeScript files that are not part of a project's TypeScript program (e.g. config files or tests excluded fromtsconfig.json) can be linted with only the non-type-aware rules instead of failing with a fatal typescript-eslint parse error.src/**/*.tsand built with Heft tolib-commonjs+lib-dts.Details
Type-aware rule separation
naming-convention,no-floating-promises,no-for-in-array) are grouped into an exportedtypeAwareRulesobject.@rushstack/eslint-config/flat/without-type-informationreturns flat-config objects that disable type-aware parsing (project: false) and turn those rules off for a given set offiles.disabledTypeAwareRulesin the helper is typed asRecord<keyof typeof typeAwareRules, 'off'>(withtypeAwareRulesusingas const satisfies Linter.RulesRecord), so adding a type-aware rule to the profile becomes a compile error until it is also disabled in the helper.TypeScript conversion
src/**and are built withdecoupled-local-node-rigto CommonJS (export =), preserving the existing subpath entry points (e.g.@rushstack/eslint-config/flat/profile/node-trusted-tool) via apackage.jsonexportsmap. CommonJS output is retained because these modules are consumed viarequire().ESLint.Plugintyping (small plugin changes)typescript-eslint's
RuleModuletype is intentionally not structurally assignable to ESLint'sRuleDefinition, so a plugin object authored with typescript-eslint's types cannot be placed in a flat-configpluginsmap without a widening cast. To keep that cast out of the config, the@rushstack/eslint-plugin,@rushstack/eslint-plugin-security, and@rushstack/eslint-plugin-packletsdefault exports are now typed asESLint.Plugin(a type-only change; runtime exports are unchanged). Only the genuinely third-party@typescript-eslintplugin retains a single localized cast, which widens throughobject(neverunknown).How it was tested
heft build --cleanpasses (TypeScript compile + self-lint) for@rushstack/eslint-configand the three@rushstackESLint plugins.rush build --to @rushstack/heft-lint-pluginbuilds cleanly through the full rig chain (local-node-rig -> local-eslint-config ->@rushstack/eslint-config+ plugins), confirming consumers still resolve the newexportsmap and lint identically.rush changefiles added for all affected published packages.