Skip to content

Since v10.0.0 cannot lint Vue with export type #2702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
2 tasks done
Shinigami92 opened this issue Mar 5, 2025 · 11 comments · Fixed by vuejs/vue-eslint-parser#254
Closed
2 tasks done

Since v10.0.0 cannot lint Vue with export type #2702

Shinigami92 opened this issue Mar 5, 2025 · 11 comments · Fixed by vuejs/vue-eslint-parser#254

Comments

@Shinigami92
Copy link
Contributor

Shinigami92 commented Mar 5, 2025

Checklist

  • I have tried restarting my IDE and the issue persists.
  • I have read the FAQ and my problem is not listed.

Tell us about your environment

  • ESLint version: 9.21.0
  • eslint-plugin-vue version: 10.0.0
  • Vue version: 3.5.13
  • Node version: 22.14.0
  • Operating System: macos

Please show your full configuration:

import { includeIgnoreFile } from "@eslint/compat";
import eslint from "@eslint/js";
import eslintPluginStylistic from "@stylistic/eslint-plugin";
import eslintPluginVitest from "@vitest/eslint-plugin";
import type { Linter } from "eslint";
import eslintPluginFileProgress from "eslint-plugin-file-progress";
import eslintPluginImportX from "eslint-plugin-import-x";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
import eslintPluginVue from "eslint-plugin-vue";
import { resolve } from "node:path";
import tseslint from "typescript-eslint";
import eslintParserVue from "vue-eslint-parser";

const gitignorePath = resolve(import.meta.dirname, ".gitignore");

const eslintRules: Linter.RulesRecord = {
  "arrow-body-style": ["error", "as-needed"],
  curly: "error",
  eqeqeq: ["error", "always", { null: "ignore" }],
  "no-else-return": "error",
};

const tsRules: Linter.RulesRecord = {
  // https://typescript-eslint.io/rules/no-unused-vars/#how-to-use
  "no-unused-vars": "off",
  "@typescript-eslint/no-unused-vars": [
    "error",
    {
      args: "all",
      argsIgnorePattern: "^_",
      caughtErrors: "all",
      caughtErrorsIgnorePattern: "^_",
      destructuredArrayIgnorePattern: "^_",
      varsIgnorePattern: "^_",
      ignoreRestSiblings: true,
    },
  ],

  // Opinionated configuration
  "@typescript-eslint/array-type": [
    "error",
    { default: "array-simple", readonly: "generic" },
  ],
  "@typescript-eslint/consistent-type-exports": "error",
  "@typescript-eslint/consistent-type-imports": [
    "error",
    {
      disallowTypeAnnotations: false,
      fixStyle: "separate-type-imports",
      prefer: "type-imports",
    },
  ],
  "@typescript-eslint/no-inferrable-types": [
    "error",
    { ignoreParameters: true },
  ],
};

const config = tseslint.config(
  //#region global
  includeIgnoreFile(gitignorePath),
  {
    name: "manual ignores",
    ignores: [
      "eslint.config.ts",
      "packages/storybook/.storybook",
      "packages/storybook/scripts/storybook-publish.mjs",
    ],
  },
  {
    name: "linter options",
    linterOptions: {
      reportUnusedDisableDirectives: "error",
    },
  },
  //#endregion

  //#region eslint (js)
  eslint.configs.recommended,
  {
    name: "eslint overrides",
    rules: eslintRules,
  },
  //#endregion

  //#region typescript-eslint
  ...tseslint.configs.recommendedTypeChecked,
  ...tseslint.configs.stylisticTypeChecked,
  {
    name: "typescript-eslint overrides",
    plugins: {
      "@typescript-eslint": tseslint.plugin,
    },
    languageOptions: {
      ecmaVersion: "latest",
      sourceType: "module",
      parserOptions: {
        parser: tseslint.parser,
        project: "./tsconfig.json",
        warnOnUnsupportedTypeScriptVersion: false,
      },
    },
    rules: {
      ...eslintRules,

      // TODO cquadflieg 2024-10-11: These are even double enabled by typescript-eslint
      "no-cond-assign": "off",
      "no-constant-binary-expression": "off",
      "no-control-regex": "off",
      "no-empty": "off",
      "no-fallthrough": "off",
      "no-prototype-builtins": "off",
      "no-unused-private-class-members": "off",
      "no-useless-escape": "off",

      // TODO cquadflieg 2024-10-30: Investigate later if these should be re-enabled (included in recommendedTypeChecked)
      "@typescript-eslint/await-thenable": "off",
      "@typescript-eslint/no-duplicate-type-constituents": "off",
      "@typescript-eslint/no-empty-object-type": "off",
      "@typescript-eslint/no-explicit-any": "off",
      "@typescript-eslint/no-floating-promises": "off",
      "@typescript-eslint/no-implied-eval": "off",
      "@typescript-eslint/no-misused-promises": "off",
      "@typescript-eslint/no-redundant-type-constituents": "off",
      "@typescript-eslint/no-this-alias": "off",
      "@typescript-eslint/no-unnecessary-type-assertion": "off",
      "@typescript-eslint/no-unsafe-argument": "off",
      "@typescript-eslint/no-unsafe-assignment": "off",
      "@typescript-eslint/no-unsafe-call": "off",
      "@typescript-eslint/no-unsafe-function-type": "off",
      "@typescript-eslint/no-unsafe-member-access": "off",
      "@typescript-eslint/no-unsafe-return": "off",
      "@typescript-eslint/no-unused-expressions": "off",
      "@typescript-eslint/no-wrapper-object-types": "off",
      "@typescript-eslint/only-throw-error": "off",
      "@typescript-eslint/prefer-promise-reject-errors": "off",
      "@typescript-eslint/require-await": "off",
      "@typescript-eslint/restrict-plus-operands": "off",
      "@typescript-eslint/restrict-template-expressions": "off",
      "@typescript-eslint/unbound-method": "off",

      // TODO cquadflieg 2024-10-11: Investigate later if these should be re-enabled (included in stylisticTypeChecked)
      "@typescript-eslint/class-literal-property-style": "off",
      "@typescript-eslint/dot-notation": "off",
      "@typescript-eslint/no-empty-function": "off",
      "@typescript-eslint/prefer-regexp-exec": "off",
      "@typescript-eslint/prefer-string-starts-ends-with": "off",

      ...tsRules,
    },
  },
  //#endregion

  //#region import
  eslintPluginImportX.flatConfigs.recommended,
  eslintPluginImportX.flatConfigs.typescript,
  {
    name: "import overrides",
    languageOptions: {
      parser: tseslint.parser,
      ecmaVersion: "latest",
      sourceType: "module",
    },
    rules: {
      // TODO cquadflieg 2024-10-18: Enable in separate MR
      "import-x/default": "off",
      "import-x/no-named-as-default": "off",

      // Opinionated configuration
      "import-x/consistent-type-specifier-style": ["error", "prefer-top-level"],
    },
    settings: {
      "import-x/extensions": [".d.ts", ".js", ".mdx", ".mts", ".ts", ".vue"],
      "import-x/parsers": {
        "@typescript-eslint/parser": [".d.ts", ".mdx", ".mts", ".ts"],
        "vue-eslint-parser": [".vue"],
      },
    },
  },
  //#endregion

  //#region stylistic
  {
    name: "stylistic overrides",
    plugins: {
      "@stylistic": eslintPluginStylistic,
    },
    rules: {
      "@stylistic/padding-line-between-statements": [
        "error",
        { blankLine: "always", prev: "block-like", next: "*" },
      ],
      "@stylistic/quotes": ["error", "double", { avoidEscape: true }],
    },
  },
  //#endregion

  //#region prettier
  eslintPluginPrettierRecommended,
  //#endregion

  //#region eslint-plugin-vue
  ...eslintPluginVue.configs["flat/recommended"],
  {
    name: "vue overrides",
    files: ["*.vue", "**/*.vue"],
    languageOptions: {
      ecmaVersion: "latest",
      sourceType: "module",
      parser: eslintParserVue,
      parserOptions: {
        parser: tseslint.parser,
        project: "./tsconfig.json",
        extraFileExtensions: [".vue"],
      },
    },
    rules: {
      ...eslintRules,

      // TODO cquadflieg 2024-10-11: Investigate later if these should be re-enabled (included in stylisticTypeChecked)
      "@typescript-eslint/consistent-type-definitions": "off",
      "@typescript-eslint/non-nullable-type-assertion-style": "off",

      ...tsRules,

      // Not needed, because it's handled by prettier
      "vue/html-indent": "off",
      "vue/max-attributes-per-line": "off",
      "vue/singleline-html-element-content-newline": "off",

      // If we use `v-html`, we know what we are doing
      "vue/no-v-html": "off",

      // TODO cquadflieg 2024-10-25: Enable in separate MR
      "vue/no-template-shadow": "off",
      "vue/require-default-prop": "off",
      "vue/require-prop-types": "off",

      // Opinionated configuration
      "vue/attribute-hyphenation": [
        "error",
        "always",
        {
          ignore: ["ariaDescribedby", "innerHTML"],
        },
      ],
      "vue/block-lang": [
        "error",
        {
          script: {
            lang: "ts",
          },
        },
      ],
      "vue/block-order": [
        "error",
        {
          order: ["script:not([setup])", "script[setup]", "template", "style"],
        },
      ],
      "vue/define-macros-order": [
        "error",
        {
          order: [
            "defineOptions",
            "defineProps",
            "defineEmits",
            "defineModel",
            "defineSlots",
          ],
          defineExposeLast: true,
        },
      ],
      "vue/html-self-closing": [
        "error",
        {
          html: {
            component: "always",
            normal: "never",
            void: "always",
          },
          svg: "always",
          math: "always",
        },
      ],
    },
  },
  //#endregion

  {
    name: "test overrides",
    files: ["*.test.ts", "**/*.test.ts"],
    plugins: {
      vitest: eslintPluginVitest,
    },
    rules: {
      "vue/one-component-per-file": "off",

      ...eslintPluginVitest.configs.recommended.rules,

      "vitest/expect-expect": "off",
      "vitest/no-alias-methods": "error",
      "vitest/prefer-each": "error",
      "vitest/prefer-to-have-length": "error",
      "vitest/valid-expect": ["error", { maxArgs: 2 }],
    },
    settings: {
      vitest: {
        typecheck: true,
      },
    },
  },

  //#region file-progress
  eslintPluginFileProgress.configs.recommended
  //#endregion
);

export default config;

What did you do?

Updated from 9.33.0 to v10.0.0

<script lang="ts">
// a comment
// another comment
export type FilterEditorListOption<
  TValue = string,
  TValueKey extends string = "value",
  TTextKey extends string = "text",
  THtmlKey extends string = "html",
  TDisabledKey extends string = "disabled",
> = Record<TValueKey, TValue> &
  Partial<Record<TTextKey, string>> &
  Partial<Record<THtmlKey, string>> &
  Partial<Record<TDisabledKey, boolean>> &
  Partial<Record<string, unknown>>;

export interface FilterEditorListProps<
  TValue = string,
  TValueKey extends string = "value",
  TTextKey extends string = "text",
  THtmlKey extends string = "html",
  TDisabledKey extends string = "disabled",
> {
  disabledField?: TDisabledKey;
  htmlField?: THtmlKey;
  textField?: TTextKey;
  valueField?: TValueKey;
  // more props
}

// omitted
</script>

<script
  setup
  lang="ts"
  generic="
    TValue = string,
    TValueKey extends string = 'value',
    TTextKey extends string = 'text',
    THtmlKey extends string = 'html',
    TDisabledKey extends string = 'disabled'
  "
>
const props = withDefaults(
  defineProps<
    FilterEditorListProps<
      TValue,
      TValueKey,
      TTextKey,
      THtmlKey,
      TDisabledKey
    >
  >(),
  {
    // @ts-expect-error: default is compatible with generic default
    disabledField: "disabled",
    // @ts-expect-error: default is compatible with generic default
    htmlField: "html",
    // @ts-expect-error: default is compatible with generic default
    textField: "text",
    // @ts-expect-error: default is compatible with generic default
    valueField: "value",
    // omitted
  }
);

// omitted
</script>

<template>
  <div>
    <!-- omitted -->
  </div>
</template>

What did you expect to happen?

Linting fine as in 9.33.0

What actually happened?

Error shows a conflict with @typescript-eslint/no-unused-vars on line 4 related to the type-keyword: export type FilterEditorListOption<

Need to find out if this has something todo with generics, or if it also happens on other places.

Oops! Something went wrong! :(

ESLint: 9.21.0

TypeError: Cannot read properties of undefined (reading 'type')
Occurred while linting /Users/shini/project/packages/app/src/components/filters/FilterEditorList.vue:4
Rule: "@typescript-eslint/no-unused-vars"
    at /Users/shini/project/node_modules/.pnpm/@[email protected]_@[email protected][email protected]__2a2173ae08b530503a9273d67d87e2f1/node_modules/@typescript-eslint/eslint-plugin/dist/util/collectUnusedVariables.js:332:28
    at Array.some (<anonymous>)
    at isExported (/Users/shini/project/node_modules/.pnpm/@[email protected]_@[email protected][email protected]__2a2173ae08b530503a9273d67d87e2f1/node_modules/@typescript-eslint/eslint-plugin/dist/util/collectUnusedVariables.js:322:26)
    at UnusedVarsVisitor.collectUnusedVariables (/Users/shini/project/node_modules/.pnpm/@[email protected]_@[email protected][email protected]__2a2173ae08b530503a9273d67d87e2f1/node_modules/@typescript-eslint/eslint-plugin/dist/util/collectUnusedVariables.js:120:21)
    at UnusedVarsVisitor.collectUnusedVariables (/Users/shini/project/node_modules/.pnpm/@[email protected]_@[email protected][email protected]__2a2173ae08b530503a9273d67d87e2f1/node_modules/@typescript-eslint/eslint-plugin/dist/util/collectUnusedVariables.js:133:18)
    at UnusedVarsVisitor.collectUnusedVariables (/Users/shini/project/node_modules/.pnpm/@[email protected]_@[email protected][email protected]__2a2173ae08b530503a9273d67d87e2f1/node_modules/@typescript-eslint/eslint-plugin/dist/util/collectUnusedVariables.js:50:36)
    at collectVariables (/Users/shini/project/node_modules/.pnpm/@[email protected]_@[email protected][email protected]__2a2173ae08b530503a9273d67d87e2f1/node_modules/@typescript-eslint/eslint-plugin/dist/util/collectUnusedVariables.js:603:30)
    at collectUnusedVariables (/Users/shini/project/node_modules/.pnpm/@[email protected]_@[email protected][email protected]__2a2173ae08b530503a9273d67d87e2f1/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unused-vars.js:279:65)
    at Program:exit (/Users/shini/project/node_modules/.pnpm/@[email protected]_@[email protected][email protected]__2a2173ae08b530503a9273d67d87e2f1/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unused-vars.js:439:36)
    at ruleErrorHandler (/Users/shini/project/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1160:48)
 ELIFECYCLE  Command failed with exit code 2.

Repository to reproduce this issue

Trying to reproduce it here: https://github.com/Shinigami92/vue-eslint-2702
But right now I could not, so I need to investigate more what the cause is...
Got it! Reproducible is now it the third commit: Shinigami92/vue-eslint-2702@167481a

@Shinigami92
Copy link
Contributor Author

@Shinigami92
Copy link
Contributor Author

ping @JoshuaKGoldberg & @bradzacher 👀

@Shinigami92
Copy link
Contributor Author

Shinigami92 commented Mar 5, 2025

@FloEdelmann
Copy link
Member

Might also be related to changes in https://github.com/vuejs/vue-eslint-parser/releases/tag/v10.0.0, which happened alongside https://github.com/vuejs/eslint-plugin-vue/releases/tag/v10.0.0.

@ota-meshi
Copy link
Member

I have fixed vue-eslint-parser. Could you please try using [email protected]?

https://github.com/vuejs/vue-eslint-parser/releases/tag/v10.1.1

@Shinigami92
Copy link
Contributor Author

I have fixed vue-eslint-parser. Could you please try using [email protected]?

vuejs/vue-eslint-parser@v10.1.1 (release)

Yes, looks like the original issue is fixed 🚀
Just now get something about console is not defined 👀, but that might be something unrelated or misconfigured in the repro 🤷.

Thanks for the quick fix ✨

@Shinigami92
Copy link
Contributor Author

Updated version v10 is now running in our company code and the time for one full Lint run is reduced from ~6min to ~2min 🚀
That's a 300% performance boost to our pipeline ❤

@FloEdelmann
Copy link
Member

I can confirm this: Our pipeline went from a painstakingly long ~27min to ~7min 🥳⚡

@cope
Copy link

cope commented Mar 14, 2025

I have fixed vue-eslint-parser. Could you please try using [email protected]?
vuejs/vue-eslint-parser@v10.1.1 (release)

Yes, looks like the original issue is fixed 🚀 Just now get something about console is not defined 👀, but that might be something unrelated or misconfigured in the repro 🤷.

Thanks for the quick fix ✨

I'm getting the same console is not defined as well as a bunch of others, how did you fix it?

@cope
Copy link

cope commented Mar 14, 2025

Got it.

add globals

Image

@FloEdelmann
Copy link
Member

I added a link to https://eslint.org/docs/latest/use/configure/language-options#predefined-global-variables in the release notes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants