Replies: 15 comments 40 replies
-
Thanks for writing this up @nzakas! Speaking only for myself as a typescript-eslint team member, I'm really excited (as I think you can guess) to move forward with this dialog. And the positive shoutouts are appreciated 😄. We'll each look at this and post back soon. A couple clarifying questions in the meantime...
|
Beta Was this translation helpful? Give feedback.
-
I'm going to split different topics up into different threads since we're on discussions (sorry if it's too spammy). For:
If you aren't using type-aware linting, my impression is that this is already how |
Beta Was this translation helpful? Give feedback.
-
If types as comments is adopted, how would that change your strawman? If anything, the industry is rallying around typescript type syntax and standardizing features like decorators. As an eslint user, I would certainly welcome having fewer dependencies in my projects. |
Beta Was this translation helpful? Give feedback.
-
TypeScript accepts a virtual file system (hence how it works in the browser, in testing, etc, which you note); Do you know makes this "involved" for these integrators? We do have Additionally, if type-aware linting is not in scope, there shouldn't be any need to use the FS at all (which IIRC is already how it works today, noted in #18830 (comment)). |
Beta Was this translation helpful? Give feedback.
-
Hey folks -- I've noticed the comments have mostly been asking questions about the problems I've noted and how the integrators work, but there hasn't really been any feedback on the ideas that I mentioned. I'd love to hear if people think the proposed direction makes sense or not. |
Beta Was this translation helpful? Give feedback.
-
Note: I'll respond to each point on its own as I have time to sit down and write down my thoughts.
I don't love the idea, personally. As I mentioned in the other thread - I don't think there's a lot of benefit to a new parser given it immediately cuts you off from type-aware linting. Sure - you could be faster with a new parser. Though I don't know if adding another parser into the ecosystem is a good investment of limited bandwidth. Esp considering that typescript-eslint exists and can do pure-syntactic, non-type-aware parsing already. Additionally worth noting that typescript-eslint's parser is used in at least one very prominent case where the parser (along with TS itself) is bundled up and shipped to users -- prettier. Prettier has a pre-release bundling step that allows it to ship a zero-dependency package to npm. A lot of the power behind typescript-eslint's approach is that when TS ships new syntax all we need to do is add a conversion step to "estree-ize" the TS AST node. The effort is minimal for us! It also means that generally people can use a TS beta release with our tooling and it works for free. They can lint the new syntax by just bumping their TS version. Which is invaluable because it allows the TS team itself to build and use new syntax - whilst still linting their code with typescript-eslint without any interruption! You mention that both oxlint and biome have their own bespoke parsers and that's true. Why didn't they just use swc and standardise the ecosystem? From what I understand:
In short - for both projects they did not have an existing solution which solved for their requirements and thus using an existing parser would have meant compromising on their offering. One counterpoint I'd like to put out there - dprint is a formatter written in rust and it uses SWC for its parsing. A custom parser wasn't necessary for them because the existing offering worked just fine. One thing I'd like to address is using babel as the TS parser. Whilst babel (and thus This is an important note because it means that essentially using babel as your eslint parser for TS code is "undefined behaviour" and will likely break your linting experience. In the beginning there was a loose attempt to keep the two projects aligned. However they are separate projects with separate requirements and separate release cycles. And ultimately the two projects have gone in different directions and thus babel is not compatible with much of the ecosystem. The spec defined here is the canonical spec that typescript-eslint produces from its parser and is thus the spec that the ecosystem lints against. In summary: I don't think it's a good use of bandwidth to do this when there is already existing solutions that eslint can adopt without compromising on its offering to users. |
Beta Was this translation helpful? Give feedback.
-
That being said - it passes all eslint-scope tests (except those for intentionally removed things) - that was one of the main ways that I ensured compatibility when I was building it. So it is a drop-in replacement for It's definitely possible to rewrite the rewrite into JS so that upstream could be updated to match - but you'd likely just be better off just making it the canonical implementation, IMO. You'll understand what I mean if you compare the two implementations side by side. The thing I'd caution is that you really want the scope manager to live very close to the parser. The scope analysis codebase is really tightly integrated with the AST structure - so changes to the AST need to be considered and implemented in both the parser and the scope analyser at the same time. By this I mean that moving |
Beta Was this translation helpful? Give feedback.
-
Thinking of all these all together: yes! This sounds great - with the separately-mentioned caveat (#18830 (comment)) that TypeScript-based parsing should live alongside scope analysis. As noted in Confusing duplication of rules, being able to deduplicate many of the extension rules from typescript-eslint would be hugely beneficial for users and quite nice for developers. There are 17 non-deprecated extension rules without type information. However, one complication is the 8 non-deprecated extension rules with type information (plus ~3 open issues accepting PRs for type-aware extension rules at time of filing). Just making core rules aware of TS syntax wouldn't be enough to fix the confusion around rule extensions. It'd just move the boundary from:
One practical way to solve this would be to build in a pluggable concept of a "types provider" into ESLint itself. This was mentioned in #16557 -> #16557 (comment) & #16557 (reply in thread):
I can't think of any other way to eliminate the duplication of rules between core & TypeScript. If some rules require type information to work, it sounds logical to me that building type awareness into core would be a requirement for them not having to be built elsewhere. |
Beta Was this translation helpful? Give feedback.
-
👍 if and only if we're also merging the parser and scope manager. Similar to what I said about the scope manager I think all the tools need to be co-located or it's not worth moving anything. Similarly we also leverage |
Beta Was this translation helpful? Give feedback.
-
One thing that's important to note is that TS does a new release to
It's a constant ~3 month cycle. This is important because it means that every ~3 months new syntax might be added -- new syntax that must be added to the parser. This is different to the current maintenance burden that ESLint maintains with acorn/espree because the JS syntax spec changes much less frequently than the TS one does. This is important to note in relation to my above two posts about the scope manager and visitor keys -- keeping everything in sync every 3 months is much easier to do if they're all in the same repo. Being able to submit PR(s) to a single repo to add support for the next TS version and being able to run all the tests for all the packages in one go is an amazing DevX -- it creates a very high confidence that things are working correctly. Whether that's the typescript-eslint monorepo or the eslint rewrite monorepo - doesn't matter - just as long as it's all co-located. |
Beta Was this translation helpful? Give feedback.
-
One thing I'd like to callout here is that the syntax of TS is a superset of JS1. This means that any parser that can parse TS can also parse JS. So there needn't be some "us vs them" parsing setup -- it needn't be "you either configure the JS language or the TS language" because really they can just be one thing. This is why our (typescript-eslint's) recommended setup for typescript-first codebases has been "just use our parser for all TS/JS files". What I mean by this is that in the hypothetical case of ESLint owning a parser, scope manager, etc that supports TS syntax -- there isn't a distinction between "JS" and "TS" support -- it's all one thing! Users shouldn't need to add This would be my vision for the future. If I were to hit the fork button on ESLint right now it would be what I'd do (i.e. swap out the
By merging all our non-type-aware extensions into the base rules to remove the Footnotes
|
Beta Was this translation helpful? Give feedback.
-
TypeScript linting performance is a major concern for my projects, especially with the "no-cycle" rule. Honestly, we don't need ESLint to handle type checking since TSC can be run in a separate CI job. ESLint should focus purely on linting, regardless of the language. There’s still a lot of room for performance improvement in ESLint, which is a common concern. Faster TypeScript linting would be a big win. Tools like SWC already do this well—offering great speed for what TSC should handle. While SWC lacks type checking, it doesn't matter; TSC can always run separately for that. I believe ESLint should adopt a similar approach: prioritize speed by focusing only on linting and leave type checking to other tools. |
Beta Was this translation helpful? Give feedback.
-
Sorry for the delay. Personally I support the proposal of integrating support for TypeScript syntax into ESLint. There's an ongoing effort to add support for languages other than JavaScript, like JSON, Markdown, etc., and it just makes sense to support TypeScript as well since it is so common in the ecosystem. +1 for updating built-in rules to make sure that they work well with TypeScript - that's a big todo! I also understand @nzakas' concerns about relying on In my understanding, a precondition to move forward is specifying the AST of TypeScript files in ESLint: basically, what ESTree has done for JavaScript. Then any custom parser or rule that follows that specification would be supported by ESLint. I believe that typescript-eslint has a package ast-spec for that purpose, and that's the only package I'd like to take away from them to make it a shared effort. |
Beta Was this translation helpful? Give feedback.
-
Would the new typescript scope and visitor keys packages differ from |
Beta Was this translation helpful? Give feedback.
-
Is there a difference in typescript-eslint's parsing performance when type-aware linting is enabled vs when it is not enabled? If there is a difference, we should compare the performance of the new parser we'd build with the performance of typescript-eslint when type-aware linting is not enabled. How much improvement in performance do we expect to consider this to be a tangible benefit? |
Beta Was this translation helpful? Give feedback.
-
Over the past few months, I’ve been having discussions with various folks, including users, plugin developers, and commercial integrators (those who are integrating ESLint into commercial products) about the future of ESLint. One of the most frequently asked questions is, “What are you doing about TypeScript support?” I typically tell them that typescript-eslint is the recommended solution, and the responses ranged from “I already use that and it’s not a smooth experience” to “we can’t use that” (explained more below).
To this point, I’ve been content to tell people to use typescript-eslint and leave it at that, however, some of the more recent conversations have led me to think that our approach may need to change.
IMPORTANT: None of what I share here should be taken as criticism of typescript-eslint or the folks behind it. They’ve done an amazing job working within the ecosystem to establish solid, dependable TypeScript support for ESLint. If anything, it’s been the core’s slow adoption of anything TypeScript-related that has led to the problems I’ll discuss.
Problems
To get an idea of where I’m coming from, it helps to outline some of the problems with the current approach to supporting TypeScript:
tsc
, it also requires a file system to operate. While there are options for creating a virtual file system to appeasetsc
, this is a more involved step than integrators want to take on (especially since cloud-based integrators have their own virtual file systems). This is a top complaint of commercial integrators. Update: As noted below, a file system isn't needed unless type-aware linting is enabled.tsc
. Commercial integrators also don’t necessarily runnpm install
in the projects they work on. ESLint is integrated directly into their product and they don’t want to have to integratetsc
to be able to use typescript-eslint.parseForESLint()
as a way for parsers to hook into ESLint. Typescript-eslint is still using this entrypoint, which means the parser is doing much more than just parsing. Unfortunately, that makes it a black box to the ESLint core, making it difficult to gather useful metrics about what is actually happening when ESLint is run.All of these problems lead to a subpar experience when linting TypeScript using ESLint. It works, most of the time, for most people, but it seems like most folks believe it could be a lot better.
A possible approach
Keeping all of these problems in mind, here is what I think might make sense as a possible way forward. This is just a brainstorm and not a fully thought out proposal, but I think it helps to have a strawman to poke at:
@eslint/js
. As we move all of our JavaScript-related functionality into@eslint/js
and thejs
language, we can also create ats
language in the same package that switches over to TypeScript. Users could then just configure their.ts
files,etc., to thejs/ts
language.Advantages to this approach
I think there are several advantages to this approach:
tsc
to lint TypeScript files, and environments wheretsc
can’t be installed, as with cloud products, will still be able to lint TypeScript files.Disadvantages to this approach
Of course, there are also disadvantages to this approach:
tsc
and typescript-eslint work on an entire project, they both have more visibility into what is happening across files. We will lose this knowledge with this approach.And what about typescript-eslint?
In case it’s not clear from the rest of this post, the intent here is not to replace typescript-eslint, only to eliminate the duplication of effort between the core and typescript-eslint. In this regard, my thinking is that typescript-eslint continues on in its present form, just without needing to worry so much about things that are now available in the core (rules, eslint-scope, visitor keys, etc.). Ideally, the ESLint team and the typescript-eslint team would collaborate on these common infrastructure pieces while typescript-eslint would continue to pave the way forward for advanced features like type-aware linting.
So, the “core” TypeScript support would be the basic level and typescript-eslint would be the premium level. The core would not have any TypeScript-specific rules, we’d still leave those for typescript-eslint.
This is just a brainstorm!
So, this is my current thinking around how to tackle the problems with TypeScript support today. As I said earlier, this is not a full proposal, nor has it gone through the sort of deep analysis we require during the RFC process to figure out if something makes sense. My intent here is to just share a possible direction and to gather feedback from the community around it.
Does this make sense to people? What other advantages or disadvantages did I miss? Are there other problems I neglected to mention that would or would not be addressed by this approach? Is there another approach that would make more sense?
I know that topics like this can elicit strong emotions, so I’d like to ask everyone to please keep the conversation civil and constructive.
Tagging: @eslint/eslint-team @JoshuaKGoldberg @bradzacher @JamesHenry @sandersn @RyanCavanaugh @jakebailey
Beta Was this translation helpful? Give feedback.
All reactions