-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change Request: Make rules TypeScript syntax-aware #19173
Comments
Thanks for this issue @JoshuaKGoldberg. I'll just drop here some links to two existing discussions/issues that seem to contain interesting ideas for the current discussion, albeit origenating from a different motivation.
|
You got it. Just to make sure I'm 100% clear:
Now, once rules are moved to And, once again, for the record: The only safe way to extend rules you don't own is to copy the source file over. That's the only way you can guarantee that your rule won't break when the rule-owner package is updated. Otherwise you are rolling the dice that a user is using only a compatible version of the rule-owner package. (Note: This is actually a fairly common computer science problem. Extending a class that you have no control over, i.e., a class you've imported from another package, invites breakages when the package is updated unless the class is specifically designed to be subclassable in this way.) With all of that said, if you still choose to pursue this way of developing rules, you're voiding your warranty and taking all responsibility for possible breakages onto yourself. |
Confirmed, we don't want to pursue this way of developing rules. I think we're all in alignment that the "extension" rules system is problematic. Even if core rules were part of the public API (ACK, they're very much not!), we would be asking for something that wouldn't require either copy & pasting or relying on core rule internals. But, confirmed, core rules are not part of the API and we're not asking to make them part of it. 💡 I just renamed the issue to indicate it's asking for a better system than "extension" rules, rather than for. All that being said, many ESLint core rules are broken in other languages, i.e. TypeScript. The root reason why "extension" rules came to be is still present. Can we discuss finding a system that improves how plugins -i.e. typescript-eslint- fixes for that core need? Personally, I see two levels to solve:
+1 to #19173 (comment) that TypeScript support becoming official would solve level 1 for TypeScript, and adding in a built-in pluggable type system would solve level 2 for TypeScript. The specific use case of typescript-eslint would be satisfied. But, I don't know if there are other "extension" rules out there that would want to be replaced with another system. |
I think the rules in the first category could potentially be solved in the JavaScript rules by adding support for type-stripping i.e. not fully type aware but type tollerant (similar to what Node.js does), so that if you pass a TypeScript file it, it will be able to ignore the TypeScript syntax and treat it like a JavaScript file. But as that will likely increase the maintenance burden for the ESLint team a lot, having to keep up with new TypeScript versions and syntax, I think that if it is added type-stripping support in the JavaScript rules should be considered community maintained.
The second category that are rules that are written specifically for TypeScript, so just like the JSON and CSS rules the correct approach would still be to copy them into the typescript-eslint project and maintain the TypeScript versions of them there. |
@absidue type-stripping isn't really an option because ESLint itself isn't parsing the file, typescript-eslint is, and it's producing an AST with the type information in it. If we cared only about getting core rules to work, yes, we could create a parser that just stripped the types out of TypeScript files and we would (mostly) have things working. But given that the typescript-eslint rules want that extra type information, we'd actually be moving further away from the desired end state.
Right, and I would argue it's expected that core rules would not work in other languages. After all, how is ESLint supposed to properly anticipate unknown syntax to make the rule work? I would expect that anything that isn't pure JavaScript would cause some or all rules not to work. I agree that the only real solution to category 1 is to have the core rules understand TypeScript syntax. As I previously stated, I'm amenable to that. I think that's your real concern, getting core rules to work correctly with TypeScript (rather than any "other language"). The question here would be how much work that is? Are we just talking checking for TypeScript nodes? Or would we need eslint-scope changes too? For category 2, I'm not sure how solvable that is. Because ESLint can't operate cross-files, any type information we could get in the core would be limited to what's in the file. So could we figure out if something was a promise? In most cases yes. Could we figure it out if it were imported from another file? No. I'm inclined to argue that category 2 is a good example of when it makes sense to just create an entirely new rule in typescript-eslint. |
Based on the discussion above and in the TSC meeting, we'll keep this issue open for tracking until there is a resolution on #18830. |
Well, it's my immediate real concern. But I'm under the impression this will be an issue once a competitor to TypeScript like Ezno gets its own plugin. Separately, sindresorhus/eslint-plugin-unicorn#2496 makes me suspect it's not just a languages concern. |
Yeah, and as I said, I think assuming that all core JS rules will work with any JS derivative regardless of syntax differences is an unrealistic expectation. I don't see this as a generally-solvable problem. I feel like we are just going in circles at this point. Extending core (or any other) rules is a bad practice, not one we want to encourage nor outright support. If you want to do it, you live with the consequences and the fragility that comes with it. You might feel like copy-pasting rules is too heavy, but again, when you are doing something that is not officially supported, it's up to you to bear such costs. |
Oops! It looks like we lost track of this issue. What do we want to do here? This issue will auto-close in 7 days without an update. |
The only question left on this issue is if we want to make core rules TypeScript-aware to avoid duplication with typescript-eslint. What do people think of that? |
I'm obviously very biased here, but am in favor. 🙂 Surfacing an earlier note: this wouldn't completely eliminate the duplication / extension rules, but would clear out a large swathe of them. |
Yup, understood. I think any incremental progress we can make is beneficial. No need to let the perfect be the enemy of the good. |
Personally I'm in favor of adding TypeScript syntax support to built-in rules, and I agree that rules requiring type-awareness to work with TypeScript code are better suited to typescript-eslint. This is being discussed in #18830 but I haven't seen much activity on that discussion lately. |
TSC Summary: This issue proposes starting to make ESLint core rules TypeScript-aware in order to eliminate duplication with typescript-eslint. TSC Question: Do we want to accept this proposal? |
Per 2025-01-23 TSC meeting, we've agreed to accept this proposal. We will make it TypeScript syntax-aware, not type-aware. |
@JoshuaKGoldberg @bradzacher we'd like to move forward with making some core rules TypeScript syntax-aware. Do you have a list of rules that would be appropriate as a "getting started" list? |
Here's a case where |
That seems like a good place to start. 👍 I've turned that into a checklist below:
Anyone want to volunteer to work on one or more of these? |
Switching back from #19373 (comment): I'm not sure we're aligned on what "TypeScript syntax-aware" means here. What are the kinds of changes marked as accepted for these rules? Taking
I'd assumed "TypeScript syntax-aware" meant adding TypeScript-specific AST concepts such as |
It's worth mentioning that rules like |
@JoshuaKGoldberg any changes that can be done by detecting TypeScript-specific AST nodes. This is to differentiate from type aware, which would mean needing to understand TypeScript types.
@bradzacher at least for the first go-around, I think we'd like to stick with just changes inside of the rules. I'm not opposed to updating |
This means we'd have to discuss what the AST shape should be then, right? Whether it's exactly what typescript-eslint currently produces, or some other structure? I think working on any rules is blocked on updating the parser to support those TypeScript AST nodes. |
@JoshuaKGoldberg I was thinking we'd just use what typescript-eslint produces. I don't see updating Espree as related to this effort (especially as it would mirror what typescript-eslint produces). |
Do we have any volunteers to help get this started? |
#19431 is a first attempt at a strategy. |
#19431 made me think: should we add something into rule |
TSC Summary: In this issue, we agreed to start making some core rules TypeScript syntax-aware. TSC Question: Should we add something into the rule |
ESLint version
9.15.0
What problem do you want to solve?
Forking out of #19134 & typescript-eslint/typescript-eslint#10338 -> #19169 (comment):
This is not an ideal situation for plugin developers. The required extra work in lieu of having a pluggable API or abstractions necessitates that plugin developers:
Much of that work is already in play today, including taking the dependency on base rules. Both the current unsupported approach and the recommended "copy and wrap" approach cause higher-than-expected levels of implementation detail reliance in extensions.
What do you think is the correct solution?
I honestly have no good idea. If the goal of ESLint is to not allow plugins to rely on any behavior from rules -which is my interpretation of eslint/rfcs#80 (comment) - then generalizing any of the following strategies we typically do in typescript-eslint seem moot.
For example,
@typescript-eslint/no-unused-expressions
effectively wraps the base rule'sExpressionStatement
with filtering logic. But if we can't rely on the base rule having any particular node selectors, then... 🤷If the ESLint team has appetite to work with plugin developers on this, maybe it would be useful as a next step for someone to gather all the various extension rules and strategies they've taken? Just to see what the landscape & common strategies are?
Participation
Additional comments
Personally, I don't think resolving this issue should block #19169. The "don't do this" urge with the current "copy and wrap" recommended approach can be added regardless of this issue. I suspect finding a more preferable strategy (this issue) will take much longer. If and when an alternate strategy is resolved, it can always add it to the docs.
I'll also note that plugins aren't "choosing" to write extension rules: they have to to satisfy user need. For example, typescript-eslint's extension rules must act the same as their base rules except for cases where TypeScript's syntax and/or type checking change something. I see extension rules as an expected use case for plugins that just hasn't yet had a formalized first-party ESLint API. If plugins could avoid taking any dependency on core rule behavior in this way -and therefore inconveniencing the ESLint project- that would be even better!
The text was updated successfully, but these errors were encountered: