-
-
Notifications
You must be signed in to change notification settings - Fork 398
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
Add prefer-ternary
rule
#514
Conversation
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 think we can also add new
, throw
, await
, yeild
as well.
This comment has been minimized.
This comment has been minimized.
docs/rules/prefer-ternary.md
Outdated
} | ||
else{ | ||
throw Error(456) | ||
} |
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.
This should not be a ternary even with an option.
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.
Better example should be
if (bar) {
throw new Error('foo');
} else{
throw new Error('bar');
}
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.
Sorry, I'm not fully understanding. Are the two of you disagreeing that this should be a use case or are you agreeing that it is just a bad example?
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'm saying that throw
should never be made into a ternary. @fisker is commenting about the example itself as you're throwing a number, which is not valid, and the code style is off.
docs/rules/prefer-ternary.md
Outdated
} | ||
else{ | ||
baz() | ||
} |
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.
Same as https://github.com/sindresorhus/eslint-plugin-unicorn/pull/514/files#r380260375. It's a bad practice to put void function calls in a ternary. Only function calls that return a value should be in a ternary.
Basically; If all the conditional branches assign to the same thing, it can be made a ternary, if not, we don't do anything. No options needed. |
Yep, that's what my thinking was as well. I would add return as well though or do you disagree? You could say that return would be assigning to the same value which is the function itself. |
If all the branches return, then yes, that would be possible too. |
As I understand, we can check the two branches has same node type. Same if (foo) {
bar = A;
} else {
bar = B;
} Same if (foo) {
return A;
} else {
return B;
} Same if (foo) {
throw A;
} else {
throw B;
} and same No need care what if (foo) {
return function() {};
} else {
return a.b.c.d.e.f();
} If you want, you can compare deeper and try merge the same part of if (foo) {
return a(1, 2, 3, 4);
} else {
return a(1, 2, 3, 5);
} You can simply fix to |
Another thing, I suggest alway add if (a = foo()) {return A} {return B}
// return a = foo() ? A : B
// return (a = foo()) ? A : B |
@fisker Can you give me an example of an Await statement that would trigger the rule? I am not seeing how it is any different from a normal function call which we do not want included in the rule. |
if (foo) {
await promise1;
} else {
await promise2;
} can fix to await (foo ? promise1 : promise2) |
I'm not sure whether I would agree that a ternary is preferable in that case. @sindresorhus , do you have any thoughts regarding await? |
Ideally, it should be: const something = foo ? promise1 : promise2;
await something; But I think |
This comment has been minimized.
This comment has been minimized.
rules/prefer-ternary.js
Outdated
function checkConsequentAndAlternateType(node) { | ||
const consequentType = node.consequent.body[0].type; | ||
return (consequentType === node.alternate.body[0].type && | ||
(consequentType === 'ReturnStatement' || | ||
(consequentType === 'ExpressionStatement' && checkConsequentAndAlternateExpressionStatement(node)))); | ||
} | ||
|
||
function checkConsequentAndAlternateExpressionStatement(node) { | ||
const consequentType = node.consequent.body[0].expression.type; | ||
return consequentType === node.alternate.body[0].expression.type && | ||
(consequentType === 'AssignmentExpression' ? compareConsequentAndAlternateAssignments(node) : | ||
consequentType === 'YieldExpression'); | ||
} |
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.
This part is not good, you can compare type first, something like
if (isSameType(node)) {
// Handle `AssignmentExpression`
// ...
// Handle `ReturnStatement`
// ...
// Handle `ThrowStatement`
// ...
// Handle `ExpressionStatement > AwaitExpression`
// ...
// ...
}
Co-Authored-By: fisker Cheung <lionkay@gmail.com>
This comment has been minimized.
This comment has been minimized.
…nicorn into prefer-ternary
} catch (err) { | ||
console.log(err); | ||
|
||
if (test) { | ||
throw a; | ||
} else { | ||
throw b; | ||
} |
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.
We have a trouble here, the catch-error-name
and prevent-abbreviations
fix err
to error
, and this rule is creating const error = test ? a : b;
cause Parsing error: Identifier 'error' has already been declared
.
I have no idea how to fix this.
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.
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.
Glad to see you here, you are the one add avoidCapture
(great util, BTW), do you have an idea how to fix this problem?
I was thinking use a global Map
to manage the variables created by our rules, but I'm not sure if it will work. And this may still can't solve the real problem, if there is a new variable created by other rule from another plugin.
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.
Even if we make all rules in eslint-plugin-unicorn
generate non-conflicting names, we can still hit name conflicts across eslint plugins, so I think this is a eslint
issue.
Having a plugin-global map seems like an ok workaround. (It should probably be a WeakMap<Scope, Set<string>>
like in prevent-abbreviations
)
eslint-plugin-unicorn/rules/prevent-abbreviations.js
Lines 552 to 558 in 16f6ef3
// Holds a map from a `Scope` to a `Set` of new variable names generated by our fixer. | |
// Used to avoid generating duplicate names, see for instance `let errCb, errorCb` test. | |
const scopeToNamesGeneratedByFixer = new WeakMap(); | |
const isSafeName = (name, scopes) => scopes.every(scope => { | |
const generatedNames = scopeToNamesGeneratedByFixer.get(scope); | |
return !generatedNames || !generatedNames.has(name); | |
}); |
EDIT: A rule-global map is ok for this PR. Making it plugin-global is an issue for another PR to fix.
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.
we can still hit name conflicts across eslint plugins, so I think this is a eslint issue.
Can you open an issue on https://github.com/eslint/eslint ?
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.
A note to myself, the if block can be some node's body, which will break by current fix, need investigate. Example: while (foo) if (test) {throw a} else {throw b} Wrong: while (foo) const error = test ? a : b;
throw error; Correct: while (foo) {
const error = test ? a : b;
throw error;
} |
Bump |
@jmoore914 Bump |
# Conflicts: # test/integration/test.js
Fixed #514 (comment) with this suggestion. #514 (comment) also fixed. Ready for review. |
Result on prettier codebase fisker/prettier#807 |
Fixes #486