Skip to content

[Bug]: TS types are broken #3838

Closed
@burtek

Description

@burtek
Contributor

Is there an existing issue for this?

  • I have searched the existing issues and my issue is unique
    My issue appears in the command-line and not only in the text editor

Description Overview

The types as exported in #3797 and #3836 are broken and throw TS errors while used. The package only exports named namespace of configs, which means you can't do any of the following:

import react from 'eslint-plugin-react';
import { config } from 'typescript-eslint';

export default config({
    plugin: { react },
    rules: {
        ...react.configs.recommended.rules,
        ...someMoreRules
    }
});

image

https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBFApgQwMbwGZQiOByRAZwBtgA7GAWjGIFcBzcypNGPAbgChRJY4BvOKghkMwenAC+cLDnwwAnmCKoowMFSKkKHTp2FlC8QjkQBZaIgBKtYkTgBeAZK6dEAD17wAJogzJbeAMxegAKfk44KLgaBnJCAC4BBBR0KQAaSOioWyIkiOjCuAA6UpZ0YuDxQmKkYRAQRDJfb1rcwkyi6NLik0aLJBs7QiyoyU5JAEp2IA

Expected Behavior

No TS error.

eslint-plugin-react version

v7.37.1

eslint version

v8.57.0

node version

v20.9.0

typescript version

All, tested on v5.5.2, v.5.6.2

Activity

JstnMcBrd

JstnMcBrd commented on Oct 1, 2024

@JstnMcBrd

This appears to be because only the flat configs are included in the types. You can do reactPlugin.configs.flat.recommended, but you can't access the legacy configs with reactPlugin.configs.recommended.

burtek

burtek commented on Oct 1, 2024

@burtek
ContributorAuthor

Oh, that's true (though still not in line with what's actually exported). But I still can't do plugin: { react }

burtek

burtek commented on Oct 1, 2024

@burtek
ContributorAuthor

And the flat config is not typescript-eslint compatible either...

image

JstnMcBrd

JstnMcBrd commented on Oct 1, 2024

@JstnMcBrd

Yes, I was able to add that - even the flat config types are broken. They appear to be incompatible with ESLint's types.

import type { Linter } from 'eslint';

export function reactConfig(): Linter.FlatConfig[] {
  return [
    reactPlugin.configs.flat.recommended,
    reactPlugin.configs.flat['jsx-runtime'],
  ];
}
Type '{ plugins: { react: { deprecatedRules: any; rules: { 'boolean-prop-naming': RuleModule; 'button-has-type': RuleModule; 'checked-requires-onchange-or-readonly': RuleModule; ... 99 more ...; 'void-dom-elements-no-children': RuleModule; }; configs: { ...; }; }; }; rules: { ...; }; languageOptions: { ...; }; }' is not assignable to type 'FlatConfig<RulesRecord>'.
  Types of property 'plugins' are incompatible.
    Type '{ react: { deprecatedRules: any; rules: { 'boolean-prop-naming': RuleModule; 'button-has-type': RuleModule; 'checked-requires-onchange-or-readonly': RuleModule; ... 99 more ...; 'void-dom-elements-no-children': RuleModule; }; configs: { ...; }; }; }' is not assignable to type 'Record<string, Plugin>'.
      Property 'react' is incompatible with index signature.
        Type '{ deprecatedRules: any; rules: { 'boolean-prop-naming': RuleModule; 'button-has-type': RuleModule; 'checked-requires-onchange-or-readonly': RuleModule; ... 99 more ...; 'void-dom-elements-no-children': RuleModule; }; configs: { ...; }; }' is not assignable to type 'Plugin'.
          Types of property 'configs' are incompatible.
            Type '{ recommended: { plugins: string[]; parserOptions: { ecmaFeatures: { jsx: boolean; }; }; rules: { 'react/display-name': number; 'react/jsx-key': number; 'react/jsx-no-comment-textnodes': number; 'react/jsx-no-duplicate-props': number; ... 17 more ...; 'react/require-render-return': number; }; }; all: { ...; }; 'jsx-...' is not assignable to type 'Record<string, FlatConfig<RulesRecord> | FlatConfig<RulesRecord>[] | ConfigData<RulesRecord>>'.
              Property 'recommended' is incompatible with index signature.
                Type '{ plugins: string[]; parserOptions: { ecmaFeatures: { jsx: boolean; }; }; rules: { 'react/display-name': number; 'react/jsx-key': number; 'react/jsx-no-comment-textnodes': number; 'react/jsx-no-duplicate-props': number; ... 17 more ...; 'react/require-render-return': number; }; }' is not assignable to type 'FlatConfig<RulesRecord> | FlatConfig<RulesRecord>[] | ConfigData<RulesRecord>'.
                  Type '{ plugins: string[]; parserOptions: { ecmaFeatures: { jsx: boolean; }; }; rules: { 'react/display-name': number; 'react/jsx-key': number; 'react/jsx-no-comment-textnodes': number; 'react/jsx-no-duplicate-props': number; ... 17 more ...; 'react/require-render-return': number; }; }' is not assignable to type 'ConfigData<RulesRecord>'.
                    Types of property 'rules' are incompatible.
                      Type '{ 'react/display-name': number; 'react/jsx-key': number; 'react/jsx-no-comment-textnodes': number; 'react/jsx-no-duplicate-props': number; 'react/jsx-no-target-blank': number; 'react/jsx-no-undef': number; ... 15 more ...; 'react/require-render-return': number; }' is not assignable to type 'Partial<RulesRecord>'.
                        Property ''react/display-name'' is incompatible with index signature.
                          Type 'number' is not assignable to type 'RuleEntry<any[]> | undefined'.ts(2322)

On first glance, I wonder if this is because the type appears to have some recursion. It's possible to do reactPlugin.configs.flat.recommended.plugins.react.configs.recommended.

image

The flat configs contain the legacy configs. Is that intentional? That might be the problem.

ljharb

ljharb commented on Oct 2, 2024

@ljharb
Member

@JstnMcBrd yes, that's intentional and unavoidable with the current architecture of the plugin.

burtek

burtek commented on Oct 2, 2024

@burtek
ContributorAuthor

I can probably look into fixing types today or tomorrow, no promises though

EDIT: so far wasn't able to

damisparks

damisparks commented on Oct 2, 2024

@damisparks

I am having the same issue.

      react: eslintPluginReact,
Type '{ deprecatedRules: any; rules: { 'boolean-prop-naming': Rule.RuleModule; 'button-has-type': Rule.RuleModule; 'checked-requires-onchange-or-readonly': Rule.RuleModule; ... 99 more ...; 'void-dom-elements-no-children': Rule.RuleModule; }; configs: { ...; }; }' is not assignable to type 'Omit<Plugin, "configs">'.
  Types of property 'rules' are incompatible.
    Type '{ 'boolean-prop-naming': Rule.RuleModule; 'button-has-type': Rule.RuleModule; 'checked-requires-onchange-or-readonly': Rule.RuleModule; 'default-props-match-prop-types': Rule.RuleModule; ... 98 more ...; 'void-dom-elements-no-children': Rule.RuleModule; }' is not assignable to type 'Record<string, LooseRuleDefinition>'.
      Property ''jsx-no-literals'' is incompatible with index signature.
        Type '{ meta: RuleModule["meta"]; create(context: any): (false | { ImportDeclaration(node: any): void; VariableDeclaration(node: any): void; }) & { ...; }; }' is not assignable to type 'LooseRuleDefinition'.
          Type '{ meta: RuleModule["meta"]; create(context: any): (false | { ImportDeclaration(node: any): void; VariableDeclaration(node: any): void; }) & { ...; }; }' is not assignable to type '{ create: LooseRuleCreateFunction; meta?: object | undefined; }'.
            The types returned by 'create(...)' are incompatible between these types.
              Type '(false | { ImportDeclaration(node: any): void; VariableDeclaration(node: any): void; }) & { Literal(node: any): void; JSXAttribute(node: any): void; JSXText(node: any): void; TemplateLiteral(node: any): void; }' is not assignable to type 'Record<string, Function | undefined>'.
                Type 'false & { Literal(node: any): void; JSXAttribute(node: any): void; JSXText(node: any): void; TemplateLiteral(node: any): void; }' is not assignable to type 'Record<string, Function | undefined>'.
                  Index signature for type 'string' is missing in type 'Boolean & { Literal(node: any): void; JSXAttribute(node: any): void; JSXText(node: any): void; TemplateLiteral(node: any): void; }'
karlhorky

karlhorky commented on Oct 6, 2024

@karlhorky
Contributor

Workaround

Use JSDoc-style type assertion to fix the types:

eslint.config.js

import react from 'eslint-plugin-react';

export const config = [
  {
    plugins: {
      // Type assertion is workaround for incorrect TypeScript
      // types in eslint-plugin-react
      //
      // TODO: Remove when types are fixed in eslint-plugin-react
      // - https://github.com/jsx-eslint/eslint-plugin-react/issues/3838
      react: /** @type {import('eslint').ESLint.Plugin} */ (react),

Or, if you use eslint.config.ts (ESLint 9.9.0+ experimental feature), use a TypeScript type assertion:

eslint.config.ts

import type { ESLint } from 'eslint';
import react from 'eslint-plugin-react';

export const config = [
  {
    plugins: {
      // Type assertion is workaround for incorrect TypeScript
      // types in eslint-plugin-react
      //
      // TODO: Remove when types are fixed in eslint-plugin-react
      // - https://github.com/jsx-eslint/eslint-plugin-react/issues/3838
      react: react as ESLint.Plugin,

14 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @ljharb@karlhorky@burtek@JstnMcBrd@damisparks

      Issue actions

        [Bug]: TS types are broken · Issue #3838 · jsx-eslint/eslint-plugin-react