-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
RFC: Common format to specify a type or value as a rule option #5271
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
Comments
I would suggest we make it a proper discriminated union to disambiguate the types and the configuration. Eg interface GlobalSpecifier {
type: 'global';
name: string;
}
interface ModuleSpecifier {
type: 'module';
export: string;
module: string;
}
type Specifier = GlobalSpecifier | ModuleSpecifier; |
The difficult thing for the module spec would be specifying exactly which module you're talking about. Sure it works fine for npm packages - but how do you handle relative imports? How do you handle tsconfig path mapping? Perhaps we require the module is either an exact match (eg Path mapping is a whole other issue to think about because ideally you would want to specify the absolute path once - and not specify it plus any path mapped versions. |
Hmm, that would make the types easier to work with in code, and explicitly easier to read. But I wonder if it'd get annoying for consumers using it?
I like the "stuff it" option for now. We can always add in relativity later, once we better understand how it's used in user code. Another thing to consider: users may want to use |
IMO verbose but clear is better for config. Esp since eslint configs are really written just once.
I think that sort of usecase would be outside the scope of this RFC wouldn't it? |
I don't think it'd be outside of the scope, no. Additional use case: let's say a project has several variants of Promises - I'm also wondering, maybe we should allow plain strings? Like |
Personally I'd respond the same way as with the discriminated union - configure once so verbosity is better. The problem with adding "glob" matchers like that is that if you have positive matchers, you need negative matchers too. Or else people can get stuck trying to craft the perfect string that hits all of your cases but ignores This is what I found in
Sorry, could you clarify how this RFC would apply to the rule? Are you suggesting we'd make the rule type-aware so that we can support not adding certain return types?
Same as above - one way to configure things with a clear, unambiguous schema is better than allowing shorthand. |
I think it's a great suggestion, and I'm also personally in the clear and verbose camp! PS Where can I install |
It just occurred to me that existing rules as mentioned in the OP essentially already use the single string form. So it'd be a breaking change to remove that option. |
Hi, Just for posterity, in #4436 we arrived at the following format (reformated to match this RFC): interface LocalSpecifier {
source: 'local';
typeName: string;
}
interface DefaultLibSpecifier {
source: 'default-lib';
typeName: string;
}
interface PackageSpecifier {
source: 'package';
typeName: string;
package: string;
}
type Specifier = LocalSpecifier | DefaultLibSpecifier | PackageSpecifier; IMHO, discriminated unions are the better option, for the reasons outlined in previous comments. I think the distinction of
type Specifier = LocalSpecifier | ModuleSpecifier | DefaultLibSpecifier | PackageSpecifier; Where if you don't use modules, all of your project code would be Additionally, it would be really nice if the same convention as in this RFC (whatever it ends up being) were to be adopted by eslint-plugin-functional e.g. in their rule prefer-immutable-types - CCing @RebeccaStevens as she seems to be behind that rule... |
@marekdedic yes! Agreed - looking back on my RFC as written, I agree it's too ambiguous between the different sources of specifiers. Just confirming I understand what you're proposing, there are the following types of sources, in increasing order of distance from a line of code:
Assuming we're on the same page, I like the discriminated type union as you've put it, with the addition of
interface LocalSpecifier {
entity: string;
source?: string;
specifier: "local";
}
interface ModuleSpecifier {
entity: string;
source?: string;
specifier: "module";
}
interface DefaultLibSpecifier {
entity: string;
specifier: "default-lib";
}
interface PackageSpecifier {
entity: string;
source: string;
specifier: "package";
}
type EntitySpecifier =
| LocalSpecifier
| ModuleSpecifier
| DefaultLibSpecifier
| PackageSpecifier;
type EntitySpecifierType = EntitySpecifier["type"]; Another complication: what if you wanted to target multiple types of specifiers -- say, all // Specifies three, just not from packages
{
"entity": "*Promise",
"specifier": ["local", "module", "default-lib"]
}
// Specifies all four
{
"entity": "*Promise",
"specifier": ["local", "module", "default-lib", "package"]
} ...and then we also have the backwards compatibility + ergonomics issue of a lot of rules trying that kind of "select everything everywhere all at once" with just a string: // Shorthand for the "all four" case above
"*Promise"; ...so I wonder if we should add in these types: // (add in types from above)
interface ManySpecifiers {
entity: string;
specifier: EntitySpecifierType[];
}
type EntitySpecifier =
| string
| LocalSpecifier
| ModuleSpecifier
| DefaultLibSpecifier
| PackageSpecifier
| ManySpecifiers; What do you think? It would be great to have this in for v6, which we just started working on this week. 🚀 |
Hi, Some comments on the details: I see that you added filename even to I'm sorry, but I really don't like the idea of the wildcard shorthands. Like, in general. :( I only have the point of view of As for naming, I used the names form my original PR, so definitely not married to them. My 2¢:
As far as v6 goes, I'm looking forward to it and kind of tacitly assumed that this big config redesign was supposed to be the breaking change in v6 :) |
Yeah, I think it's not useful for most rules, but in some it would be very useful. For example, you might want to target...
Agreed 😄. I can't think of any modern codebase reason to want this. The closest I can think of is the idea of targeting all But, for the sake of the other use cases for this standard format (such as the examples above), I think we'll need to throw it in to the format.
You're totally right and my proposal is technically inaccurate - module is a subset of file! ...and now I'm wondering, what use case is there for having a delineation between
Agreed. I think
Agreed in theory, but - I think it would be useful to support the
Heh, my hesitation there is that On that train of thought, words like
Haha, this is definitely top of mind & high excitement from a technical perspective! I think you're right for power users like you and me (my favorite kind of user ❤️). But for marketing purposes I'm thinking #5204 and #5900 will likely end up being emphasized more. 🥲 |
As for the usefulness of
If it's supposed to be for edge-cases and outdated code, I would say it's fine to have the user specify it manually. But if you want to do shorthands anyway, I'm much more in favour of the "the specifier can be an array" approach than the "replace the whole object with just a string" approach - the second one is too opaque and I think it would encourage users to do that even if they don't need to, because it looks like the basic format whereas IMO, it should be an edge case use.
Ha, I thought about that as well since writing my last comment, but I arrived at the opposite conclusion - there is a meaningful difference. See this (extremely simplified) example of a non-module file: function showLessonEditView() {
const lessonID = $("lesson").data("id");
showLessonName(lessonID);
// ... rest of function
}
function showLessonName(string lessonID) {
$("body").innerHTML = "This is a lesson with the ID" + lessonID;
} Even thought this is not a module, there are some variables and functions (and possibly types) that are used just in this file, like
Hmm, you're right, it does make sense to have a single name in that format. I'm still against that format overall, but if it's that way, let's keep just one name.
I think |
What worries me is that it often won't be clear which one to use. Suppose you want to ban (sorry for the delay - still very interested in this conversation!) |
Also mentioned in ESLint's discussions: eslint/eslint#16540 |
Hi, either you meant |
Whoops, sorry - edited now 😅 to say |
They are very similar, true. I'd lean towards having both, but it's a trade-off that depends on what you prioritize... |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Overview
There are quite a few times when users have a real need to change or disable a rule for specifically some package exports:
@typescript-eslint/no-floating-promises
should Ignore node 18's built-in test function #5231: At least the new built-innode:test
runner intentionally returns ignorable Promises from itstest()
method that can be ignored by the ruleallowedNames
option fromexplicit-module-boundary-types
typesToIgnore
option fromno-unnecessary-type-assertion
allowedPromiseNames
option frompromise-function-async
This RFC proposes a unified format that all rules can use to specify some types or values from the global namespace and/or packages.
Prior Art
Some rules have previously created name-based overrides, such as
typesToIgnore
fromno-unnecessary-type-assertion
. Those are sub-optimal:For example,
@typescript-eslint/no-floating-promises
even has avoid
operator to get around needing to disable the rule for specific cases. It's the workaround we currently recommend for issues such as #5231.Proposal
The current proposal is for specifying the following types of sources, in increasing order of distance from a line of code:
lib.d.ts
orlib.dom.d.ts
"lodash"
Additionally, you can specify multiple of these at the same time with type many.
An example of how this might look in practice:
See #5271 (comment) and following comments for more context.
Original format, not rich enough
I propose the format look be able to specify whether the item is from the global namespace (i.e.
globalThis
,window
) or a module (e.g."node:test"
,"./path/to/file"
):Examples
For example, specifying the global
location
object:Instead specifying the global
window.location
object:Specifying the
test
export from thenode:test
module:Specify the
SafePromise
export from some~/utils/promises
module (presumably configured by TSConfig paths to point to a file with a location like./src/utils/promises.ts
):Specify the
SafePromise
export from somemy-safe-promises
(TODO: find some unused or useful name) module:The text was updated successfully, but these errors were encountered: