Why Eslint don't see global TypeScript types in .vue files (no-undef)

Viewed 1898

I have a global type in my Vue/TypeScript project. In the .ts files, everything is fine, and Eslint sees it, but in the .vue files, Eslint throws error that the type is not defined. TypeScript does not throw any errors and sees this type. Configs and code sample below.

package.json

{
  "dependencies": {
    "@hse-design/core": "^1.1.0",
    "@hse-design/vue": "^1.1.0",
    "axios": "^0.21.1",
    "cors": "^2.8.5",
    "element-ui": "^2.12.0",
    "faker": "^5.1.0",
    "focus-visible": "^5.2.0",
    "js-cookie": "^2.2.1",
    "lodash": "^4.17.20",
    "morgan": "^1.10.0",
    "nprogress": "^0.2.0",
    "path-to-regexp": "^6.2.0",
    "register-service-worker": "^1.6.2",
    "sanitize.css": "^12.0.1",
    "vue": "^2.6.10",
    "vue-class-component": "^7.2.6",
    "vue-property-decorator": "^9.1.2",
    "vue-router": "^3.1.2",
    "vue-svgicon": "^3.2.6",
    "vuex": "^3.1.1",
    "vuex-class": "^0.3.2",
    "vuex-module-decorators": "^1.0.1",
    "yamljs": "^0.3.0"
  },
  "devDependencies": {
    "@babel/plugin-proposal-optional-chaining": "^7.12.7",
    "@babel/preset-typescript": "^7.13.0",
    "@types/compression": "^1.7.0",
    "@types/cors": "^2.8.8",
    "@types/faker": "^5.1.4",
    "@types/jest": "^26.0.21",
    "@types/js-cookie": "^2.2.2",
    "@types/lodash": "^4.14.165",
    "@types/morgan": "^1.9.2",
    "@types/node": "~14.14.35",
    "@types/nprogress": "^0.2.0",
    "@types/webpack-env": "^1.14.0",
    "@types/yamljs": "^0.2.31",
    "@typescript-eslint/eslint-plugin": "^4.22.1",
    "@typescript-eslint/parser": "^4.22.1",
    "@vue/cli-plugin-babel": "^4.5.12",
    "@vue/cli-plugin-e2e-cypress": "^4.5.12",
    "@vue/cli-plugin-eslint": "^4.5.12",
    "@vue/cli-plugin-pwa": "^4.5.12",
    "@vue/cli-plugin-typescript": "^4.5.12",
    "@vue/cli-plugin-unit-jest": "^4.5.12",
    "@vue/cli-service": "^4.5.12",
    "@vue/eslint-config-standard": "^6.0.0",
    "@vue/eslint-config-typescript": "^7.0.0",
    "@vue/test-utils": "^1.0.0-beta.29",
    "babel-core": "^7.0.0-bridge.0",
    "babel-eslint": "^10.0.3",
    "babel-loader": "^8.2.2",
    "concurrently": "^6.0.0",
    "core-js": "^3.12.0",
    "eslint": "^7.25.0",
    "eslint-config-airbnb-base": "^14.2.1",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^4.3.1",
    "eslint-plugin-standard": "^5.0.0",
    "eslint-plugin-vue": "^7.9.0",
    "fibers": "^5.0.0",
    "jest": "^26.6.3",
    "sass": "^1.22.10",
    "sass-loader": "10.1.1",
    "style-resources-loader": "^1.2.1",
    "swagger-routes-express": "^3.2.1",
    "ts-jest": "^26.5.4",
    "ts-node-dev": "^1.0.0",
    "typescript": "^4.2.3",
    "vue-cli-plugin-element": "^1.0.1",
    "vue-cli-plugin-style-resources-loader": "^0.1.3",
    "vue-template-compiler": "^2.6.10",
    "webpack": "^5.28.0"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "module": "ESNext",
    "target": "ESNext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "node",
      "jest",
      "webpack-env",
    ],
    "typeRoots": [
      "./src/@types",
      "./node_modules/@types"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [ "src/**/*", "tests/**/*" ],
  "exclude": [ "node_modules" ]
}

.eslint.js

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true,
    es6: true
  },
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
    ecmaVersion: 2020
  },
  plugins: [
    'vue'
  ],
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'class-methods-use-this': 0,
    'consistent-return': 0,
    'import/no-cycle': 0,
    'import/extensions': 0,
    'import/no-extraneous-dependencies': 0,
    'space-before-function-paren': [2, 'never'],
    'vue/array-bracket-spacing': 'error',
    'vue/arrow-spacing': 'error',
    'vue/block-spacing': 'error',
    'vue/brace-style': 'error',
    'vue/camelcase': 'error',
    'vue/comma-dangle': 'error',
    'vue/component-name-in-template-casing': 'error',
    'vue/eqeqeq': 'error',
    'vue/key-spacing': 'error',
    'vue/match-component-file-name': 'error',
    'vue/object-curly-spacing': 'error',
    'vue/max-attributes-per-line': ['error', {
      singleline: 3,
      multiline: {
        max: 1,
        allowFirstLine: false
      }
    }]
  },
  extends: [
    'eslint:recommended',
    'airbnb-base',
    'plugin:vue/recommended',
    '@vue/standard',
    '@vue/typescript'
  ]
}

src/@types/typings.d.ts

type MyType = {
  value: any
}

src/App.vue

<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'

@Component({
  name: 'App',
})
export default class extends Vue {
  myField: MyType = { value: 1 } // The error is here | Ошибка здесь
</script>

Error

error: 'MyType' is not defined (no-undef) at src/App.vue:15:12:
  13 | })
  14 | export default class extends Vue {
> 15 |   myField: MyType = { value: 1 }
     |            ^
  16 | }
  17 | </script>
  18 | 
1 Answers

The troubleshooting documentation of typescript-eslint has an entry about this:

The no-undef lint rule does not use TypeScript to determine the global variables that exist - instead, it relies upon ESLint's configuration.

We strongly recommend that you do not use the no-undef lint rule on TypeScript projects. The checks it provides are already provided by TypeScript without the need for configuration - TypeScript just does this significantly better.

They recommend to disable no-undef for TypeScript by adding an override to your ESLint configuration like so:

overrides: [
  {
    files: ['*.ts', '*.vue'],
    rules: {
      'no-undef': 'off',
    },
  },
],

Please note that this disables no-undef for all *.vue files, regardless of whether they use TypeScript or JavaScript.

Related