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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(eslint-plugin): [prefer-nullish-coalescing] ignoreStrings option #1569
feat(eslint-plugin): [prefer-nullish-coalescing] ignoreStrings option #1569
Conversation
Thanks for the PR, @Crow-EH! 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. As a thank you, your profile/company logo will be added to our main README which receives thousands of unique visitors per day. |
Codecov Report
@@ Coverage Diff @@
## master #1569 +/- ##
==========================================
+ Coverage 95.56% 95.56% +<.01%
==========================================
Files 142 142
Lines 6519 6523 +4
Branches 1855 1856 +1
==========================================
+ Hits 6230 6234 +4
Misses 105 105
Partials 184 184
|
I made an error in the doc. Will fix it asap. |
Done |
Not a maintainer, but my $0.02: It seems the main benefit of While certainly there are places where you do want to handle the falsy and the nullish case together, I can't think of any reason why a codebase would always want to do so. And, for those exceptions, I actually think it's better to override the rule with a So, in your specific case, I actually think this is good: I can tell that the intended behavior is to replace // eslint-disable-line @typescript-eslint/prefer-nullish-coalescing
a.path.that.i.cant.always?.trust || 'fallback' Or to be even more explicit: //
function replaceEmptyStringOrNullish(val: string | null | undefined, defaultVal: string) {
return val ? val : defaultValue;
}
replaceEmptyStringOrNullish(a.path.that.i.cant.always?.trust, 'fallback'); So I'd suggest that typescript-eslint should resist adding configuration for this specific case. |
I would tend to agree with @Retsam here. For rules I look to the intention of the rule when adding options, to see if the option is working toward making the rule more flexible whilst still achieving its goal. In this case, the intention of The problem with this option is that it's too far reaching - it disables it for every single nullish string; it lacks the contextual information you've provided in the post. IMO a better solution to this would be something similar to what Retsam mentioned - a utility function, though I would take it one step further. type NotEmptyString = string & { __notEmptyBrand: unknown };
/**
* Ensures that a string is not null or undefined, and
*/
function notEmptyString(value: string | null | undefined, fallback: string): NotEmptyString {
if (!fallback) { throw new Error('fallback must be a non-empty string'); }
return (value ? value : fallback) as NotEmptyString;
}
const one = notEmptyString('', 'fallback'); // typeof one === NotEmptyString
const two = notEmptyString('foo', 'fallback'); // typeof two === NotEmptyString
const three = notEmptyString(notEmptyString('foo', 'fallback'), 'fallback'); // note that you can assign NotEmptyString to anywhere that accepts string
declare function onlyAcceptsNotEmptyString(value: NotEmptyString): void;
onlyAcceptsNotEmptyString(one); // no error
declare const regularString: string;
onlyAcceptsNotEmptyString(regularString); // ERROR because string is not assignable to NotEmptyString I use this technique in our codebase to ensure that I only pass canonical paths around. The typescript codebase also uses this technique for a similar use case (which is where I got the idea from). This is an awesome way to do some form of validation on a primitive value, and represent it in your types, which means that your validation is backed by the typescript compiler - removing all ambiguity, and cognitive load. |
Hi @Retsam, @bradzacher, and thank you for your inputs ! @Retsam I agree with you that typescript-eslint should not become an impossible mess of inter-breaking configuration flags, and I also know we would only realize when it's too late, so the maintainers have to carefully pick which conf makes the cut. I honestly didn't know whether this flag was hurtful or not for the community, and my intention was always to find out here. @bradzacher I understand your point clearly, and your thought process is crystal clear. I didn't think of this rule like that (the "prefer" might have fooled me 馃槄). Your way of dealing with it is actually pretty clean. It might be a bit more verbose, but the pros overweight the cons by a lot IMO. I'll ask my teammates what they think about it. I'm OK with both of you, please close this PR whenever. PS: FYI @Retsam, we actually (try to) remove all the empty strings before sending any response back: All of the front applications are heavily "server-driven" and we wouldn't want any empty divs showing up (turns out EDIT: Actually, I'll close it. Thank you for your time :D |
Hi everyone ! 馃憢
It's my first contribution to this project, so don't hesitate to correct me if anything is wrong.
Also, I didn't find any issue about this, so I directly created a PR. Once again, just tell me if I'm doing it wrong. 馃憣
Original use case
TL;DR: I need
||
for strings, but please tell me if there's a better solution for the communityI work on a pretty big "middleware" kind of API, of which one of the main job is to clean the data from a multitude of other services, before sending it to many kind of front-end clients on different platforms.
One of its cleaning tasks is to replace any empty string with either
undefined
or a fallback value.We really want to use
prefer-nullish-coalescing
to enforce the use of??
, to avoid accidentally removingfalse
or0
, but we also don't want to replaceaString || 'fallback'
by!aString ? 'fallback' : aString
. Keep in mind that most of the time it even looks likea.path.that.i.cant.always?.trust || 'fallback'
VS!a.path.that.i.cant.always?.trust ? 'fallback' : a.path.that.i.cant.always.trust
.This problem might be a bit specific and I'm 100% open to code a better solution, answering everyone needs, if required.
Solution:
ignoreStrings
(just copying the doc I just commited)Setting this option to
true
will cause the rule to be ignored when the left-hand's TypeScript type isstring
, whether it may be nullish or not.This can be useful if you want to enforce the use of
??
on every types butstring
, because you want to handle empty strings.Incorrect code for
ignoreStrings: false
, and correct code forignoreStrings: true
:Correct code for
ignoreStrings: false
:Please tell me if there's any error, subpar code or any rephrasing needed.
Thank you very much !