简体   繁体   中英

Setting up ESLint for a Typescript Project using React, React Testing Library, and Cypress

It seems that every time I start using a new library of some sort I run into problems with ESLint, and I can never truly figure out why. I stumble through until the errors and warnings go away, and then deal with it all again later. I'm hoping that I can get some answers on how it's supposed to work here.

I have a React project that is using Typescript. We use react-testing-library for tests (with Jest types) as well as Cypress for testing (with Cypress types). Jest and Cypress will conflict as they use a lot of the same keywords but from different libraries (ie. describe , expect , context , etc.).

Furthermore, with cypress is is possible to define your own functions which will extend the global cy object that is used in all cypress tests, however this must also be typed and requires you to write your own definition files.

Everything was going well until I tried to add my own type definitions, at which point it started complaining again. (I would like to note that my project does compile and run as expected with everything I'm doing at the moment. It is simply ESLint that isn't happy).

The main issue I'm trying to solve is why the type definition doesn't seem to be included in a project. I'm receiving the following error:

Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: cypress\support\index.d.ts.
The file must be included in at least one of the projects provided.

As well as whether there is a better way to lay this out.

Thanks to anyone who actually takes the time to read this and make an attempt.

The folder structure looks like this:

cypress
- fixtures
- integrations
- - file.spec.ts <- Uses the custom functions in a test
- plugins
- support
- - commands.ts <- Contains my custom functions
- - index.d.ts  <- Types for custom functions
- - index.ts
- .eslintrc.js (A)
- tsconfig.json (B)
src (contains all the jest tests)
.eslintrc.js (C)
tsconfig.json (D)
tsconfig.eslint.json

.eslintrc.js (A)

module.exports = {
  extends: ['../.eslintrc.js'],
  parserOptions: {
    project: 'cypress/tsconfig.json',
  },
}

tsconfig.json (B)

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "noEmit": true,
    // be explicit about types included
    // to avoid clashing with Jest types
    "types": ["cypress", "cypress-axe"],
    "allowJs": true
  },
  "include": [".eslintrc.js", "../node_modules/cypress", "./**/*"]
}

.eslintrc.js (C)

module.exports = {
  parser: '@typescript-eslint/parser',
  root: true,
  env: {
    es6: true,
    node: true,
    browser: true,
  },
  parserOptions: {
    ecmaVersion: 6,
    sourceType: 'module',
    tsconfigRootDir: __dirname,
    project: './tsconfig.eslint.json',
    ecmaFeatures: {
      jsx: true,
    },
  },
  plugins: ['react', 'react-hooks', '@typescript-eslint', 'cypress'],
  rules: {
    'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
    ...
  },
}

tsconfig.json (D)

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["es6", "dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "strict": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react",
    "baseUrl": "src",
    "types": ["jest"]
  },
  "include": [
    "build-system",
    "src",
    ".eslintrc.js",
    "pretty.js",
    "gulpfile.js"
  ],
  "exclude": ["node_modules", "src/*.test.ts", "src/*.test.tsx"]
}

tsconfig.eslint.json

{
  // extend your base config so you don't have to redefine your compilerOptions
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "noEmit": true
  },
  "exclude": []
}

For what it's worth, I've solved this problem. Hopefully this can be useful to someone at some point.

Essentially the solution was to get rid of the index.d.ts file, and put the types directly in the commands.ts file.

With a little help from this thread, which states:

Hi folks, this issue comment helped me work around the issue: cypress-io/add-ypress-custom-command-in-typescript#2 (comment)

Fix seems to be declaring Cypress in the global namespace, and your custom command definitions in there (copied from ☝️ ):

declare global {
  namespace Cypress {
    interface Chainable {
      customCommand: typeof customCommand;
    }
  }
}

function customCommand(input: MyCustomClass) {
  // ...
}

Cypress.Commands.add('customCommand', customCommand);

But agree that the solution suggested in the docs doesn't work

So, by adding the the following code to my commands.ts file:

declare global {
  namespace Cypress {
    interface Chainable<Subject> {
      /**
       * Custom command to wait for a specific set of network requests to finish
       * @example cy.waitForData()
       */
      waitForData(): Chainable<Subject>
    }
  }
}

as well as adding the following rule to my.eslintrc.js (C) file:

rules: {
  ...    
  '@typescript-eslint/no-namespace': ['off']
}

I was able to fix the issue.

Furthermore, if it's relevant, the isolatedModules flag will require that you add an export {} to the commands.ts file as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM