-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Feature: Disallow non-boolean type for inline If with logical && operator #2073
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
This is a tricky one, because if it’s any falsy value besides a number, it will work properly. Once the nullish coalescing operator lands, and that’s a better alternative, it might make more sense to have a rule - right now, such a rule would probably be more noisy than helpful, I’m afraid. |
I managed to miss this issue, and implemented a rule as suggested here by @cburgmer in PR #2077. It protects solely against using the truthiness of a length prop as guard, which as @ljharb said in the PR might be too narrow to be useful. I don’t see an immediately obvious way of expanding the rule to cover more numeric guard cases, but my gut feeling is that the .length case is common enough to justify the rule. Then again, it might be that I want to believe that just so I’m not alone in being a schmuck (did exactly this mistake in production code twice)... |
Relying on the implicit truthiness of |
Hmm. Interesting. I was focused on the fact that implicit length truthiness is especially dangerous in JSX, earning me the title ”0 of the week” on the main company whiteboard. But I see your point - you’ll live a happier life if you never rely on it, jsx or not. Still, in most other environments it’s more arrogant than unsafe to do it, no? There I’d classify this rule as ”coding style”, while in JSX it’s a ”potential error” (actually pretty much a guaranteed one). @ljharb, would you consider this rule if we add a ”forbidEverywhere” option, disallowing boolean casting of dot length anywhere as per the airbnb styleguide? |
Another way of making the rule more useful could be to allow the user to add props other than |
I don't think it's really something a linter can do - there's no guarantee a length property is even a number (not that I can think of a use case for that). I think at some point the solution is always going to boil down to actual tests. |
A linter would've saved me, twice! But then again, so would writing better code. :) I agree there's no guarantee, but I think the argument is that there doesn't need to be, and that erroneous casting of Also, protecting against this particular mistake with tests seems a bit off - would you really write a test to see that you didn't render a zero? But I'm liking the idea of enforcing the AirBnB rule of never casting length to boolean, and so we'll probably widen the rule to do that and deploy it to our linting suite outside of the React package. Thank you for the input! |
I might have misunderstood, the only linting rule I've sound so far is https://github.com/sindresorhus/eslint-plugin-unicorn/blob/master/docs/rules/explicit-length-check.md, which is not included in the Airbnb rule set. To reproduce:
|
@ljharb: I want to make a last attempt at convincing you! In the 2.5 months passed I've seen the To me the bottom line is this: we have a very common pattern in normal JS that might be frowned upon by some, but it is still widely used and perfectly safe. In JSX it suddenly isn't safe anymore. So yes, you could argue that users should use a general explicit-length-check rule like the one linked by @cburgmer above. But to me that rule enforces an opinion, which falls in a different category. The proposed jsx rule catches the usage in JSX specifically, which is always a mistake. |
Half a year later, new assignment, ran this rule on the (large) codebase. Found 10 violations, all of which were real dangers of rendering a 0 to screen. Codebase is otherwise in very good shape with sound linting, and the devs are experienced and driven. I'm even more convinced than before that this rule deserves a place in a React-dedicated rules package. |
I'd like a rule for that as well. I'm not sure if this package treats react-native as first class citizen, but if it does, another point to consider is that a lint rule like this would actually prevent errors in react-native. Unlike in the browser, you can't render text outside of {
props.text && <Text>{props.text}</Text>
} This would just render an empty string and just won't affect a react app. In react native however, we'd be trying to render a string outside of a Text component too. |
Where is the rule? You refer to the Will a more broader rule be considered? Especially in the react-native env such rule would help to prevent serious bugs in the code base. Maybe it could only be implemented as a typescript rule, because the TS compiler knows about types (and RN projects are usually type script projects). |
I've seen eslint rules in conjunction with TypeScript. Maybe the rule I'm looking will benefit from the type information. Would such a rule still be welcome here? |
@cburgmer as long as it's still useful with no type information, absolutely. |
One solution is to use Alternative solution which doesn't require types would be to create a rule which disallows |
that wouldn’t be appropriate tho when a is a boolean type :-/ |
Why though? I often use ternary in JSX even when the else branch is just null. I like it because it's consistent. It also kinda forces you to at least consider displaying some message like "No data to show" which is usually a nice UX. I once had to refactor a bunch of components to show these empty states in UI and if I used ternaries from the beginning it would be much easier. It would be useful rule for enforcing style, and if you want actual bug-catching rule then there's strict-boolean-expressions. |
"a nice UX" depends very much on the context. |
Just to share a painful learning from today: The The following JSX <span>
{false ? "whatever : ""}
"my caption"
<span> will result in this HTML (quotes used to highlight the text nodes)
|
@cburgmer to be fair tho, relying on XPath queries like that is inherently brittle, so the flaw there isn't use of the empty string. |
Would very much like a rule that enforces ternary usage over |
To enforce ternary in JSX you can use {
"no-restricted-syntax": [
"error",
{
"selector": "JSXElement > JSXExpressionContainer > LogicalExpression[operator!='??']",
"message": "Please use ternary operator instead"
}
]
} |
Just adding to @phaux's suggestion above:
|
@marneborn your example does not work. Try this: {
"no-restricted-syntax": [
"error",
{
"selector": ":matches(JSXElement, JSXFragment) > JSXExpressionContainer > LogicalExpression[operator!='??']",
"message": "Please use ternary operator instead"
}
]
} |
It would still be great to have a rule to detect non-boolean type for inline rendering, without needing to enforce ternary expressions. I thought the goal was to spot code like this: <div>
{unreadMessages.length && <h2>Hey</h2>}
</div> rather than banning any logical expression with && in JSX. Is there any way to achieve this with custom rules? |
@ljharb I don't think the new rule should do this. |
@MichaelDeBoey right. and i'm saying that that would be very harmful, because quite often, |
How does that matter here? The discussion has somewhat derailed in the past days, as said the TS rule works perfectly and not by "banning" |
@rijk a component that takes numbers > 0, for example, is perfectly safe to use |
@ljharb It is indeed safe to use, but that's not the point of this request imo. |
I understand that. I'm saying that such a rule would be actively harmful since it would be penalizing the common case for the sake of an edge case. |
You call it "penalizing the common case", but some people will see it as "consistent behavior", as that way they don't need to think about the fact if it safe or not. Having this rule, but disabling it by default is a good idea imo, as that will leave the people the active/determined choice if they want this consistency or not. |
You can already opt into it with |
Yep, that’s what what good coding styles do! For example, a lot of folks use eqeqeq to write The same principle applies here. If |
@kachkaev and if this were general javascript, the principle would apply. This is React - where we basically always have type information about the props - so we can accurately warn about many of the cases. |
I don’t understand what you mean, sorry. How would ‘information about props’ work here? const MyComponent = () => {
const something = useSomething();
return <div>{something && <Something />}</div>;
} Remember, that TypeScript is out of scope – that’s separate subject: #2073 (comment). The advantage of a new JS-only rule over |
Yes, in that specific case, we'd have no idea what kind of thing it is - and I think the best choice is to not warn on it, or at least, to offer a suggestion but not a warning. I think it would be very reasonable to have a rule that, when it knew the type with certainty, provided a warning or was silent, but when it didn't, only provided a suggestion. |
If the rule has at least the option to always report, I wouldn't be against it. But as @kachkaev already mentioned with the |
Oh, I missed that that was what you were arguing. In that case, I agree with @ljharb ; why force people to use ternaries if there is another way (using I personally like having the {editable && <EditButton />} I find that definitely easier to read and parse than: {editable ? <EditButton /> : undefined} Especially when you have a longer piece of code; {editable ? (
<div>
<EditButton
onClick={() = {
// ...
}}
/>
</div>
) : undefined} you have to scan all the way to the bottom to find out that it is actually just an |
I don't want to force people automatically, I want to make it an auto-fixable option. I'm also more in favor of using
You can always invert the condition so you'll have {condition ? null : <Component />} Although I wouldn't do that in your given example as I like to keep my conditions as positive as possible (this is a bit smaller cognitive load). |
This is handled by #3203 |
Thanks a lot! Tried it and the which is autofixed to
|
Ah ok... will file one if none exist later. |
Nope, that should be fine. |
Since there is no testcase for multiple conditions I guess this a bug then? |
This was already fixed in #3299 but hasn't been released yet. You can see in that PR some new tests to make sure several boolean chained are correct. |
As suggested in typescript-eslint/typescript-eslint#2770 (comment), we ended up adding this ESLint plugin: eslint-plugin-jsx-expressions - npm, to detect & prevent |
We are making use of inline conditional guard statements a lot like:
We often run into bugs where a string literal
0
is rendered, because a short hand notion was used, which react does not allow:React will not render a boolean value, while it does for integer values like 0 and 1.
If possible, I'd like to detect this pattern and flag it early. Apologies if this already exists, I checked for "conditional" and "inline" and couldn't find a match.
The text was updated successfully, but these errors were encountered: