带有 TypeScript 和 Firebase 的 ESLint - 无 undef 错误

ESLint with TypeScript and Firebase - no-undef error

我在我的项目中使用 eslinttypescript-eslint 来检查 TypeScript 代码。
我对 firebase-admin 提供的关于 no-undef 规则的导入有疑问。

环境:

"firebase-admin": "9.4.1"
"eslint": "7.14.0"
"@typescript-eslint/eslint-plugin": "4.8.2"
"@typescript-eslint/parser": "4.8.2"
"typescript": "4.1.2"

规则:

no-undef:
 - error
 - typeof: true

所有规则(.eslintrc):

root: true
env:
  es6: true
  node: true
  jest/globals: true
extends:
  - 'eslint:recommended'
  - google
  - 'plugin:@typescript-eslint/eslint-recommended'
  - 'plugin:@typescript-eslint/recommended'
  - 'plugin:@typescript-eslint/recommended-requiring-type-checking'
  - 'plugin:import/errors'
  - 'plugin:import/warnings'
  - 'plugin:import/typescript'
  - 'plugin:jest/style'
  - 'plugin:jest/all'
  - 'plugin:jsdoc/recommended'
  - 'plugin:prettier/recommended'
globals:
  Atomics: readonly
  SharedArrayBuffer: readonly
parserOptions:
  ecmaVersion: 2018
  sourceType: module
  project: ./tsconfig.json
parser: '@typescript-eslint/parser'
plugins:
  - '@typescript-eslint'
  - typescript-sort-keys
  - jest
  - prettier
  - progress
  - jsdoc
settings:
  import/extensions:
    - .js
    - .ts
  import/parsers:
    '@typescript-eslint/parser':
      - .ts
  import/cache:
    lifetime: Infinity
rules:
  progress/activate: 1
  jest/no-done-callback: error
  jest/no-disabled-tests: off
  '@typescript-eslint/member-ordering':
    - error
    - default:
        - signature
        - public-static-field
        - protected-static-field
        - private-static-field
        - public-static-method
        - protected-static-method
        - private-static-method
        - public-abstract-field
        - protected-abstract-field
        - private-abstract-field
        - public-instance-field
        - protected-instance-field
        - private-instance-field
        - public-constructor
        - protected-constructor
        - private-constructor
        - public-instance-method
        - protected-instance-method
        - private-instance-method
        - public-abstract-method
        - protected-abstract-method
        - private-abstract-method
  jest/lowercase-name:
    - error
    - ignore:
        - describe
  jest/no-hooks:
    - error
    - allow:
        - beforeAll
        - beforeEach
        - afterAll
        - afterEach
  typescript-sort-keys/interface:
    - error
    - asc
    - caseSensitive: true
      natural: true
  typescript-sort-keys/string-enum:
    - error
    - asc
    - caseSensitive: true
      natural: true
  quotes: off
  no-else-return:
    - error
    - allowElseIf: true
  sort-keys:
    - error
    - asc
    - caseSensitive: true
      natural: true
      minKeys: 2
  '@typescript-eslint/quotes':
    - error
    - backtick
  '@typescript-eslint/no-var-requires': off
  '@typescript-eslint/explicit-function-return-type': off
  '@typescript-eslint/no-unused-vars':
    - error
    - argsIgnorePattern: ^_
  import/no-namespace: off
  import/first: error
  import/exports-last: error
  import/no-duplicates: error
  import/no-commonjs: error
  import/no-dynamic-require: error
  import/order:
    - error
    - groups:
      - index
      - sibling
      - parent
      - internal
      - external
      - builtin
      - object
      newlines-between: never
      alphabetize:
        order: asc
        caseInsensitive: false
  indent: off
  no-duplicate-imports: error
  sort-imports: off
  new-cap:
    - error
    - capIsNewExceptions:
      - StoreConfig
  '@typescript-eslint/naming-convention':
    - error
    - selector: default
      format:
        - camelCase
    - selector: variable
      format:
        - camelCase
        - UPPER_CASE
    - selector: variable
      types:
        - boolean
      format:
        - PascalCase
        - UPPER_CASE
      prefix:
        - is
        - has
        - should
        - contains
        - as
        - to
    - selector: parameter
      format:
        - camelCase
      leadingUnderscore: allow
    - selector: parameter
      types:
        - boolean
      format:
        - PascalCase
      leadingUnderscore: allow
      prefix:
        - is
        - has
        - should
        - contains
        - as
        - to
    - selector: function
      format:
        - camelCase
    - selector: memberLike
      modifiers:
        - private
      format:
        - camelCase
      leadingUnderscore: require
    - selector: memberLike
      modifiers:
        - protected
      format:
        - camelCase
      leadingUnderscore: require
    - selector: typeLike
      format:
        - PascalCase
    - selector: typeParameter
      format:
        - PascalCase
      prefix:
        - T
    - selector: interface
      format:
        - PascalCase
      prefix:
        - I
    - selector: typeAlias
      format:
        - PascalCase
      prefix:
        - I
    - selector: enumMember
      format:
        - UPPER_CASE
    - selector: enum
      format:
        - PascalCase
      suffix:
        - Enum
  '@typescript-eslint/no-explicit-any':
    - error
    - fixToUnknown: false
      ignoreRestArgs: false
  object-curly-spacing:
    - error
    - always
  linebreak-style: off
  computed-property-spacing:
    - error
    - never
  array-bracket-spacing: off
  prefer-rest-params: off
  require-jsdoc: off
  max-len: off
  comma-dangle: off
  object-shorthand:
    - error
    - always
    - avoidExplicitReturnArrows: true
  arrow-body-style:
    - error
    - as-needed
    - requireReturnForObjectLiteral: true
  no-dupe-else-if: error
  no-unmodified-loop-condition: error
  no-eval: error
  no-extra-label: error
  no-await-in-loop: error
  prefer-destructuring:
    - error
    - VariableDeclarator:
        array: true
        object: true
      AssignmentExpression:
        array: true
        object: true
    - enforceForRenamedProperties: false
  '@typescript-eslint/unbound-method': off
  '@typescript-eslint/no-unsafe-call': off
  '@typescript-eslint/no-unsafe-member-access': off
  '@typescript-eslint/no-unsafe-return': off
  '@typescript-eslint/no-unsafe-assignment': off
  '@typescript-eslint/prefer-enum-initializers': error
  '@typescript-eslint/prefer-literal-enum-member': off
  '@typescript-eslint/unified-signatures': error
  '@typescript-eslint/ban-ts-comment': error
  no-undef-init: error
  no-magic-numbers:
    - error
    - enforceConst: true
      detectObjects: false
  prettier/prettier:
    - error
    - printWidth: 120
      useTabs: false
      semi: true
      singleQuote: true
      quoteProps: consistent
      trailingComma: es5
      bracketSpacing: true
      arrowParens: always
      endOfLine: lf
      tabWidth: 2
  import/no-unresolved: error
  prefer-template: error
  no-useless-concat: error
  no-constant-condition:
    - error
    - checkLoops: true
  prefer-promise-reject-errors:
    - error
    - allowEmptyReject: false
  no-case-declarations: error
  no-irregular-whitespace:
    - error
    - skipStrings: false
      skipComments: false
      skipRegExps: false
      skipTemplates: false
  '@typescript-eslint/adjacent-overload-signatures': error
  '@typescript-eslint/no-misused-promises':
    - error
    - checksConditionals: true
      checksVoidReturn: true
  no-invalid-this: off
  '@typescript-eslint/no-invalid-this': error
  prefer-const:
    - error
    - destructuring: any
      ignoreReadBeforeAssign: false
  camelcase: off
  valid-jsdoc: off
  'jsdoc/require-jsdoc': warn # @todo change it to error but we should fix all current jsdoc errors
  curly:
    - error
    - all
  '@typescript-eslint/prefer-regexp-exec': error
  '@typescript-eslint/await-thenable': error
  '@typescript-eslint/prefer-readonly': error
  '@typescript-eslint/triple-slash-reference':
    - error
    - path: never
      types: never
      lib: never
  '@typescript-eslint/prefer-readonly-parameter-types': # @todo change it to error once the nested object issue was resolved (https://github.com/typescript-eslint/typescript-eslint/issues/2823)
    - warn
    - ignoreInferredTypes: true
      checkParameterProperties: false
  '@typescript-eslint/no-empty-interface':
    - error
    - allowSingleExtends: false
  '@typescript-eslint/restrict-template-expressions':
    - error
    - allowNumber: false
      allowBoolean: false
      allowAny: false
      allowNullish: false
  '@typescript-eslint/ban-types': error
  '@typescript-eslint/restrict-plus-operands':
    - error
    - checkCompoundAssignments: true
  '@typescript-eslint/no-floating-promises':
    - error
    - ignoreVoid: true
  no-undef:
    - warn # @todo change it to error 
    - typeof: true
  no-useless-escape: error
  no-prototype-builtins: error
  require-await: off
  '@typescript-eslint/require-await': error
  no-empty-function: off
  no-var: error
  spaced-comment:
    - error
    - always
    - markers:
        - /
  '@typescript-eslint/no-empty-function':
    - error
    - allow:
        - decoratedFunctions
overrides:
  - files:
      - '*.js'
    rules:
      '@typescript-eslint/explicit-module-boundary-types': off
      import/no-commonjs: off
  - files:
      - '*.ts'
    rules:
      '@typescript-eslint/explicit-function-return-type':
        - error
        - allowExpressions: false
          allowTypedFunctionExpressions: false
          allowHigherOrderFunctions: false
      '@typescript-eslint/no-var-requires': error
  - files:
      - '*.spec.ts'
    rules:
      no-magic-numbers: off
      '@typescript-eslint/ban-types':
        - error
        - types:
            Readonly:
              message: 'Sonia rule - This is useless inside the tests'
          extendDefaults: true
      '@typescript-eslint/naming-convention':
        - error
        - selector: default
          format:
            - camelCase
        - selector: variable
          format:
            - camelCase
            - UPPER_CASE
        - selector: variable
          types:
            - boolean
          format:
            - camelCase
            - UPPER_CASE
        - selector: parameter
          format:
            - camelCase
          leadingUnderscore: allow
        - selector: parameter
          types:
            - boolean
          format:
            - camelCase
          leadingUnderscore: allow
        - selector: function
          format:
            - camelCase
        - selector: memberLike
          modifiers:
            - private
          format:
            - camelCase
          leadingUnderscore: require
        - selector: memberLike
          modifiers:
            - protected
          format:
            - camelCase
          leadingUnderscore: require
        - selector: typeLike
          format:
            - PascalCase
        - selector: typeParameter
          format:
            - PascalCase
          prefix:
            - T
        - selector: interface
          format:
            - PascalCase
          prefix:
            - I
        - selector: typeAlias
          format:
            - PascalCase
          prefix:
            - I
        - selector: enumMember
          format:
            - UPPER_CASE
        - selector: enum
          format:
            - PascalCase
          suffix:
            - Enum
      '@typescript-eslint/no-explicit-any': off
      '@typescript-eslint/ban-ts-comment': off

仅根据 no-undef 规则导入失败:

import admin from 'firebase-admin';
import WriteResult = admin.firestore.WriteResult;

错误:

ESLint: 'firestore' is not defined.(no-undef)

但我的项目正在运行,这对 TypeScript 有效。

基于 documentation of TypeScript ESLint,使用 globalsenv 可能是最好的解决方法。
除了我没有找到任何关于现有配置的相关信息,所以我被卡住了。

你有什么解决办法吗?谢谢。

我相信您已经链接到推荐的答案:禁用 no-undef 规则。 引用您链接的文档页面:

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.

如果您的项目中混合使用 TypeScript 和 JavaScript,最好仅对 TypeScript 文件禁用它:

overrides:
  - files: 
    - "*.ts"
    rules:
      'no-undef': off

但是,如果您仍然希望保留规则并希望消除一些特定全局变量的错误(firestore 在您的情况下),您可以按照 ESLint configuration guide for specifying globals,它列出 2 种方法:

  1. 配置注释: 在文件顶部添加/* global firestore */
  2. 配置文件:将以下内容添加到您的.eslintrc
globals:
  firestore: readonly