-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat(eslint-plugin): [interface-name-prefix, class-name-casing] Add allowUnderscorePrefix option to support private declarations #790
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
Conversation
Thanks for the PR, @octogonz! typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community. The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately. Thanks again! 🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint |
Two questions:
|
Oh man, I was hoping no one would ask that! Like most coding style opinions, my secret motivation is to avoid changing thousands of files that do it a certain way. Some are commercially supported API contracts. So I'll be transparent about that heheh. But there's also legitimate justification for
To tie it all together, suppose the |
I personally hate the I can understand it if you don't use a rule like our I still don't hugely understand why it matters if someone consumes an internal type. It's a compile time thing with no implementation, so it shouldn't matter. Unlike a class or function which might be exposing some internal implementation details, a type is just a shape. But I can kind of understand the b2b setting of commercially consumed libraries and trying to help consumers understand what they should/shouldn't access. If your packages are in a monorepo, you could actually achieve this in a better way. If you add The typescript package uses this copiously to hide a lot of things from the types without impacting their internal devx. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I won't block this.
It ultimately won't affect our recommended default of never
, nor will it harm anyone using the rule right now.
I would ask that you instead hide this new functionality behind an option, so that people have to opt-in to it. Otherwise it's a breaking change because the you're technically making the rule "less strict"
I.e. add a third string:
never
= don't useIFoo
or_IFoo
always
= useIFoo
always-allow-underscore
= useIFoo
or_IFoo
Why should that matter? The
It would be weird to use
We use that, too. (Or rather API Extractor's .d.ts rollups that support The change that I proposed doesn't /require/ anyone to use the
The typescript-eslint package is still fairly rough around the edges. Do you consider this to be a significant breaking change? I mean if we are being pedantic it should probably be a separate setting |
Because if you're consistently enforcing
I don't think it's a breaking change for For |
The debate about (BTW I had trouble finding the comments you referenced, since there's a bunch of flame wars about this topic. If you're interested to chat about it offline, I'm reachable on Gitter. It might be worth discussing, as I am involved with some influential projects, so if you can convince me to abandon
HBO and Microsoft both use the Your point seems reasonable, though. I'll add |
as does facebook - for private class members. They in fact have a transpilation step which converts underscore prefixed members to be private outside the class definition: class Foo { _priv = 1 }
// transpiles to
var Foo = function Foo() { this.$Foo_priv = 1; }; And to some degree for private exports that need to be exposed, but shouldn't be used, though in that case they usually use "goofy" naming to further show they're not to be used :P But I was referring to types specifically - I haven't seen underscored private types before in either flow or ts codebases. |
Here's an example: sp-application-base All those entries that say I totally get that such requirements may be statistically rare. It's possible these concerns are irrelevant to 99% of the humans that write TypeScript code, most of which are small causal projects. But that "1%" minority is still a lot of developers and a lot of money, just closed-source and not super visible on the internet. |
@bradzacher I am hitting this same issue with class-name-casing as well, for a package that exports an internal class name like Maybe we should try to make them consistent, e.g. add an option |
based on the current config style of this rule, I don't think it's the correct thing to add an extra object option to this. In this case we can do it easily with just another string in the enum.
|
Does ESLint support polymorphic options? In other words, this: ["error", "always"] ...as a shorthand for this: [
"error",
{
"prefixRequired": "always"
}
] ...which can be expanded to this: [
"error",
{
"prefixRequired": "always",
"allowUnderscorePrefix": true
}
] That might enable new options to be introduced without breaking compatibility with the old schema.
@bradzacher What do you want the option to be? I can implement it today and just add it to this same PR, since it's the same scope of work. |
It does support it. You'll have to setup the json schema to support it. You can use the That's a definite path to go, I didn't suggest it because it's more work: change the schema, change the options type def, add support for handling both options forms. You'd also need to duplicate tests to make sure both options forms work for existing options. If you're happy to do the extra work, then I'd love to see it, as it's the best of both worlds. Would suggest the following schema: type Options = [
| 'never'
| 'always'
| {
allowPrefix: false;
}
| {
allowPrefix: true;
allowUnderscorePrefix: boolean;
},
];
/*
'never' === { allowPrefix: false }
'always' === { allowPrefix: true, allowUnderscorePrefix: false }
*/
// keep the default backwards-compatible
defaultConfig === { allowPrefix: false } For type Options = [
{
allowUnderscorePrefix: boolean;
},
];
// keep the default backwards-compatible
defaultConfig === { allowUnderscorePrefix: false } |
I don't mind the extra work. This design seems fine. BTW it wasn't immediately obvious to me that |
definitely - someone has already brought that up #671! |
@bradzacher I'm realizing that Something like |
I went with this: type Options = [
| 'never'
| 'always'
| {
prefixWithI?: 'never';
}
| {
prefixWithI: 'always';
allowUnderscorePrefix?: boolean;
},
]; |
62b2b1f
to
44ef4a3
Compare
I pushed an update for |
88ce91c
to
26be88a
Compare
…allowUnderscorePrefix" feature
…wUnderscorePrefix" feature
26be88a
to
a58c9a8
Compare
This is ready to go, and all the checks are green. Please take a look when you get a chance. Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it all LGTM!
Thanks for doing this!
Ping... Is there anything holding up this PR from getting merged? Thanks! |
Thanks! 😁 |
The
_
prefix is sometimes used to designate a private API, in which case a private interface might benamed
_IAnimal
instead ofIAnimal
. Update "interface-name-prefix" to recognize both forms.