From f200eb262efb18985431367f9c6ac397d482fe70 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 13 Aug 2025 11:42:23 -0600 Subject: [PATCH 1/2] provide compatible types --- .../src/compatibility-types.ts | 25 +++++++ packages/typescript-eslint/src/index.ts | 66 ++++++++++++------- .../tests/type-compatibility.test-d.ts | 49 ++++++++++++++ packages/typescript-eslint/vitest.config.mts | 4 ++ 4 files changed, 122 insertions(+), 22 deletions(-) create mode 100644 packages/typescript-eslint/src/compatibility-types.ts create mode 100644 packages/typescript-eslint/tests/type-compatibility.test-d.ts diff --git a/packages/typescript-eslint/src/compatibility-types.ts b/packages/typescript-eslint/src/compatibility-types.ts new file mode 100644 index 000000000000..10357e93accf --- /dev/null +++ b/packages/typescript-eslint/src/compatibility-types.ts @@ -0,0 +1,25 @@ +/* + * This file contains types that are intentionally wide/inaccurate, that exist + * for the purpose of satisfying both `defineConfig()` and `tseslint.config()`. + * See https://github.com/typescript-eslint/typescript-eslint/issues/10899 + */ + +export interface CompatibleParser { + parseForESLint(text: string): { + ast: unknown; + scopeManager: unknown; + }; +} + +export interface CompatibleConfig { + name?: string; + rules?: object; +} + +export type CompatibleConfigArray = CompatibleConfig[]; + +export interface CompatiblePlugin { + meta: { + name: string; + }; +} diff --git a/packages/typescript-eslint/src/index.ts b/packages/typescript-eslint/src/index.ts index b123d80b4db2..02197940789a 100644 --- a/packages/typescript-eslint/src/index.ts +++ b/packages/typescript-eslint/src/index.ts @@ -1,14 +1,23 @@ // see the comment in config-helper.ts for why this doesn't use /ts-eslint import type { TSESLint } from '@typescript-eslint/utils'; +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; import pluginBase from '@typescript-eslint/eslint-plugin'; import rawPlugin from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/raw-plugin'; import { addCandidateTSConfigRootDir } from '@typescript-eslint/typescript-estree'; +import type { + CompatibleConfig, + CompatibleConfigArray, + CompatibleParser, + CompatiblePlugin, +} from './compatibility-types'; + import { config } from './config-helper'; import { getTSConfigRootDirFromStack } from './getTSConfigRootDirFromStack'; -export const parser: TSESLint.FlatConfig.Parser = rawPlugin.parser; +export const parser: CompatibleParser = + rawPlugin.parser as CompatibleParser satisfies FlatConfig.Parser; /* we could build a plugin object here without the `configs` key - but if we do @@ -33,30 +42,30 @@ use our new package); however legacy configs consumed via `@eslint/eslintrc` would never be able to satisfy this constraint and thus users would be blocked from using them. */ -export const plugin: TSESLint.FlatConfig.Plugin = pluginBase as Omit< - typeof pluginBase, - 'configs' ->; +export const plugin: CompatiblePlugin = + pluginBase satisfies FlatConfig.Plugins['string']; export const configs = createConfigsGetters({ /** * Enables each the rules provided as a part of typescript-eslint. Note that many rules are not applicable in all codebases, or are meant to be configured. * @see {@link https://typescript-eslint.io/users/configs#all} */ - all: rawPlugin.flatConfigs['flat/all'], + all: rawPlugin.flatConfigs['flat/all'] as CompatibleConfigArray, /** * A minimal ruleset that sets only the required parser and plugin options needed to run typescript-eslint. * We don't recommend using this directly; instead, extend from an earlier recommended rule. * @see {@link https://typescript-eslint.io/users/configs#base} */ - base: rawPlugin.flatConfigs['flat/base'], + base: rawPlugin.flatConfigs['flat/base'] as CompatibleConfig, /** * A utility ruleset that will disable type-aware linting and all type-aware rules available in our project. * @see {@link https://typescript-eslint.io/users/configs#disable-type-checked} */ - disableTypeChecked: rawPlugin.flatConfigs['flat/disable-type-checked'], + disableTypeChecked: rawPlugin.flatConfigs[ + 'flat/disable-type-checked' + ] as CompatibleConfig, /** * This is a compatibility ruleset that: @@ -64,65 +73,78 @@ export const configs = createConfigsGetters({ * - enables rules that make sense due to TS's typechecking / transpilation. * @see {@link https://typescript-eslint.io/users/configs/#eslint-recommended} */ - eslintRecommended: rawPlugin.flatConfigs['flat/eslint-recommended'], + eslintRecommended: rawPlugin.flatConfigs[ + 'flat/eslint-recommended' + ] as CompatibleConfig, /** * Recommended rules for code correctness that you can drop in without additional configuration. * @see {@link https://typescript-eslint.io/users/configs#recommended} */ - recommended: rawPlugin.flatConfigs['flat/recommended'], + recommended: rawPlugin.flatConfigs[ + 'flat/recommended' + ] as CompatibleConfigArray, /** * Contains all of `recommended` along with additional recommended rules that require type information. * @see {@link https://typescript-eslint.io/users/configs#recommended-type-checked} */ - recommendedTypeChecked: - rawPlugin.flatConfigs['flat/recommended-type-checked'], + recommendedTypeChecked: rawPlugin.flatConfigs[ + 'flat/recommended-type-checked' + ] as CompatibleConfigArray, /** * A version of `recommended` that only contains type-checked rules and disables of any corresponding core ESLint rules. * @see {@link https://typescript-eslint.io/users/configs#recommended-type-checked-only} */ - recommendedTypeCheckedOnly: - rawPlugin.flatConfigs['flat/recommended-type-checked-only'], + recommendedTypeCheckedOnly: rawPlugin.flatConfigs[ + 'flat/recommended-type-checked-only' + ] as CompatibleConfigArray, /** * Contains all of `recommended`, as well as additional strict rules that can also catch bugs. * @see {@link https://typescript-eslint.io/users/configs#strict} */ - strict: rawPlugin.flatConfigs['flat/strict'], + strict: rawPlugin.flatConfigs['flat/strict'] as CompatibleConfigArray, /** * Contains all of `recommended`, `recommended-type-checked`, and `strict`, along with additional strict rules that require type information. * @see {@link https://typescript-eslint.io/users/configs#strict-type-checked} */ - strictTypeChecked: rawPlugin.flatConfigs['flat/strict-type-checked'], + strictTypeChecked: rawPlugin.flatConfigs[ + 'flat/strict-type-checked' + ] as CompatibleConfigArray, /** * A version of `strict` that only contains type-checked rules and disables of any corresponding core ESLint rules. * @see {@link https://typescript-eslint.io/users/configs#strict-type-checked-only} */ - strictTypeCheckedOnly: rawPlugin.flatConfigs['flat/strict-type-checked-only'], + strictTypeCheckedOnly: rawPlugin.flatConfigs[ + 'flat/strict-type-checked-only' + ] as CompatibleConfigArray, /** * Rules considered to be best practice for modern TypeScript codebases, but that do not impact program logic. * @see {@link https://typescript-eslint.io/users/configs#stylistic} */ - stylistic: rawPlugin.flatConfigs['flat/stylistic'], + stylistic: rawPlugin.flatConfigs['flat/stylistic'] as CompatibleConfigArray, /** * Contains all of `stylistic`, along with additional stylistic rules that require type information. * @see {@link https://typescript-eslint.io/users/configs#stylistic-type-checked} */ - stylisticTypeChecked: rawPlugin.flatConfigs['flat/stylistic-type-checked'], + stylisticTypeChecked: rawPlugin.flatConfigs[ + 'flat/stylistic-type-checked' + ] as CompatibleConfigArray, /** * A version of `stylistic` that only contains type-checked rules and disables of any corresponding core ESLint rules. * @see {@link https://typescript-eslint.io/users/configs#stylistic-type-checked-only} */ - stylisticTypeCheckedOnly: - rawPlugin.flatConfigs['flat/stylistic-type-checked-only'], -}); + stylisticTypeCheckedOnly: rawPlugin.flatConfigs[ + 'flat/stylistic-type-checked-only' + ] as CompatibleConfigArray, +}) satisfies Record; function createConfigsGetters(values: T): T { const configs = {}; diff --git a/packages/typescript-eslint/tests/type-compatibility.test-d.ts b/packages/typescript-eslint/tests/type-compatibility.test-d.ts new file mode 100644 index 000000000000..b0e2f71b81d2 --- /dev/null +++ b/packages/typescript-eslint/tests/type-compatibility.test-d.ts @@ -0,0 +1,49 @@ +import { defineConfig } from 'eslint/config'; + +import tseslint from '../src/index'; + +describe('test for compatibility with config helpers', () => { + test('exported plugin is compatible with tseslint.config()', () => { + tseslint.config({ + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + }); + }); + + test('exported plugin is compatible with defineConfig()', () => { + defineConfig({ + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + }); + }); + + test('exported parser is compatible with tseslint.config()', () => { + tseslint.config({ + languageOptions: { + parser: tseslint.parser, + }, + }); + }); + + test('exported parser is compatible with defineConfig()', () => { + defineConfig({ + languageOptions: { + parser: tseslint.parser, + }, + }); + }); + + test('exported configs are compatible with tseslint.config()', () => { + tseslint.config(tseslint.configs.recommendedTypeChecked); + tseslint.config(tseslint.configs.strict); + tseslint.config(tseslint.configs.eslintRecommended); + }); + + test('exported configs are compatible with defineConfig()', () => { + defineConfig(tseslint.configs.recommendedTypeChecked); + defineConfig(tseslint.configs.strict); + defineConfig(tseslint.configs.eslintRecommended); + }); +}); diff --git a/packages/typescript-eslint/vitest.config.mts b/packages/typescript-eslint/vitest.config.mts index 7d1cc31b83d7..a527ce206583 100644 --- a/packages/typescript-eslint/vitest.config.mts +++ b/packages/typescript-eslint/vitest.config.mts @@ -14,6 +14,10 @@ const vitestConfig = mergeConfig( dir: path.join(import.meta.dirname, 'tests'), name: packageJson.name, root: import.meta.dirname, + typecheck: { + enabled: true, + tsconfig: path.join(import.meta.dirname, 'tsconfig.spec.json'), + }, }, }), ); From 6dd9d709f40a04af426cac09b6162776fab09721 Mon Sep 17 00:00:00 2001 From: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:30:28 -0600 Subject: [PATCH 2/2] nx stuff? --- packages/typescript-eslint/package.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/typescript-eslint/package.json b/packages/typescript-eslint/package.json index 3b828ed2dd7c..ed9e55806bd5 100644 --- a/packages/typescript-eslint/package.json +++ b/packages/typescript-eslint/package.json @@ -78,6 +78,18 @@ "targets": { "lint": { "command": "eslint" + }, + "typecheck": { + "outputs": [ + "{workspaceRoot}/dist", + "{projectRoot}/dist" + ] + }, + "test": { + "dependsOn": [ + "^build", + "typecheck" + ] } } }