vue-cli: Typescript-Eslint conflict

Version

3.0.0-rc.3

Reproduction link

https://github.com/marcus-blaettermann-ventoro/vue-cli-bug/tree/master

Steps to reproduce

Clone the given repo, npm install and then npm run lint

Alternative: Run vue create with those settings:

Vue CLI v3.0.0-rc.3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, Linter
? Use class-style component syntax? No
? Use Babel alongside TypeScript for auto-detected polyfills? No
? Pick a linter / formatter config: Airbnb
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N)

Then add this to the src/components/Hello-World.vue:

enum Direction {
Up,
Down,
}

const foo: Direction = Direction.Up;

What is expected?

The linter should not find any errors, or at least should fix the errors it finds.

What is actually happening?

The linter adds a space before one of the commas in the enum Direction and then complains about it. Removing the space has no effect because the linter adds it the next time again. When using lint on save in an editor, the comma jumps between the first and second line.


  • This has been reproduced on both Mac and Windows.
  • The problem doesn`t occure with the same code in a regular .ts file
  • The problem only occurs, when an enum (something regular JavaScript doesn`t have) followed by something with a type declaration.
  • The problem also occurs, when the enum is in a single line. enum Direction { Up, Down }
  • If the const foo : Direction = Direction.Up; is removed the linter doesn`t mind the enum anymore.
  • I think the linter somehow chokes on the type declaration colon.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 3
  • Comments: 17 (12 by maintainers)

Commits related to this issue

Most upvoted comments

Hi.

Recently, I fixed several bugs in typescript-eslint-parser: https://github.com/eslint/typescript-eslint-parser/pull/540

Also, I added the support of typescript-eslint-parser into our online demo: https://mysticatea.github.io/vue-eslint-demo/

Would you confirm whether the problem was fixed or not?

Oh boy, I went through typescript-eslint-parser, eslint and vue-eslint-parser internals, but finally I know what’s going on 😅

So:

  • typescript-eslint-parser produces new property typeAnnotation on some nodes in the AST with dedicated token - called TSTypeAnnotation
  • vue-eslint-parser in this scenario uses parser service offered by typescript-eslint-parser to get the enhanced AST
  • one of the responsibilities of vue-eslint-parser, due to the fact that we’re parsing .vue files and script tag is usually placed somewhere below template is to update locations and ranges of all tokens in the AST, so that errors produced by eslint point to exact place in our .vue file.
  • Unfortunately vue-eslint-parser doesn’t know anything about these new types of tokens, and doesn’t update their ranges.
  • space-infix-ops rule happens to handle case with this typeAnnotation, and if it detects this annotation it takes this exact node and based on it’s range and range of the former tag finds all tokens that happens to be within the calculated range
  • beacause the token of TSTypeAnnotation type has wrong range - it reports wrongly discovered tokens, hence the error pointed in the description.

I’m going to come up with the fix as soon as I can 😃

Fun fact: You can fix it by placing <script> tag at the top of your .vue file 😃

I just confirmed this issue, and did some digging in ASTs. Looks like the produced AST for the <script> tag inside .vue file is almost identical to what is produced by plain .ts file. Almost - because one thing differs and causes the problem. In .vue AST there are extra circular parent nodes. In effect - whenever eslint meets VariableDeclarator it’ll search through the whole range of tokens inside the file, whereas in .ts there is no parrent to corrupt token search range.

In ESlint v5 the parent node behaviour has slightly changed and now the parent node is being set before we even have access to the AST in eslint rules. I’ll try to investigate it a bit more, but it looks like the AST for .vue might be the proper one given those changes and the problem might be in the space-infix-ops rule itself. Also we may have another problem - that plain javascript files are not being parsed consistently if it happens to be true.

I’ll post an update once I know more. These are my first insights.

@mysticatea I can confirm it’s working now. Thank you.