Skip to content
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

Merged
merged 56 commits into from Sep 29, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
0e75c80
Added rule and tests
jmoore914 Jan 26, 2020
efc968a
Added documentation
jmoore914 Feb 7, 2020
3fffe86
Tweaked language; adding throw, new, yield, await
jmoore914 Feb 14, 2020
80a3a15
Added tests and changed fix function
jmoore914 Feb 17, 2020
3b4d6ab
Added new options to docs; made yield false by default
jmoore914 Feb 17, 2020
b210f43
Delete launch.json
sindresorhus Feb 17, 2020
eb7aace
Simplified rule
jmoore914 Feb 29, 2020
d3a4527
Merge branch 'prefer-ternary' of https://github.com/jmoore914/eslint-…
jmoore914 Feb 29, 2020
52f5344
Added await; check to make sure not nested ternary
jmoore914 Mar 3, 2020
d59f2b4
Update rules/prefer-ternary.js
jmoore914 Mar 3, 2020
bd9e21c
Works without braces
jmoore914 Mar 3, 2020
f0bdc34
Linting
jmoore914 Mar 3, 2020
48a26aa
More linting
jmoore914 Mar 3, 2020
73a2263
More linting
jmoore914 Mar 3, 2020
c600475
Added additional tests
jmoore914 Mar 6, 2020
8beb75e
Merge remote-tracking branch 'upstream/master' into prefer-ternary
jmoore914 Mar 13, 2020
f75101e
Added support for yield*
jmoore914 Mar 13, 2020
64e9a0b
Linting
jmoore914 Mar 13, 2020
503493c
Merge branch 'master' into prefer-ternary
fisker Mar 16, 2020
5860a73
fix style
fisker Mar 16, 2020
a325e56
fix integration test
fisker Mar 16, 2020
3dfcd8e
Merge branch 'master' into prefer-ternary
fisker Mar 26, 2020
25abee5
Merge remote-tracking branch 'remotes/upstream/master' into prefer-te…
fisker May 5, 2020
a99e7ae
Ignore `IfStatement.test=ConditionalExpression`
fisker May 5, 2020
6a3c793
Improve `getNodeBody`
fisker May 5, 2020
ee7f888
Fix logic
fisker May 5, 2020
b8dd909
Nested merge
fisker May 6, 2020
59b9b97
Crazy cases
fisker May 6, 2020
51515d8
Fix lower precedence
fisker May 6, 2020
6688591
Add `()` to yield
fisker May 6, 2020
8c3a70f
Update examples
fisker May 6, 2020
133c2fa
More tests
fisker May 6, 2020
9a7fc8e
Style
fisker May 6, 2020
428b3c0
Test `operator`
fisker May 7, 2020
59a7970
Ignore `IfStatement.alternate`
fisker May 7, 2020
da5dfd5
Handle `return/yield` undefined
fisker Jun 2, 2020
e588687
Fix throw
fisker Jun 2, 2020
aca1f1e
Clean up
fisker Jun 2, 2020
efff08c
Code style
fisker Jun 2, 2020
003acc3
Update docs
fisker Jun 2, 2020
8768b41
Comment
fisker Jun 2, 2020
76c2378
Merge branch 'master' into prefer-ternary
fisker Jun 2, 2020
89906b2
Merge branch 'prefer-ternary' of github.com:jmoore914/eslint-plugin-u…
fisker Jun 2, 2020
853acf9
Fix lint
fisker Jun 2, 2020
397c749
Add extra integration test
fisker Jun 2, 2020
f39f38f
Move to top
fisker Jun 2, 2020
23974b3
ignore
fisker Jun 2, 2020
5b99354
Fix error message
fisker Jun 2, 2020
7cb5ee2
Coverage
fisker Jun 2, 2020
1d06eeb
Merge branch 'master' into prefer-ternary
fisker Sep 27, 2020
dd81f37
Fix test
fisker Sep 27, 2020
cee4f4d
Handle `IfStatement` is body of some node
fisker Sep 27, 2020
b37b4a9
Fix code style
fisker Sep 27, 2020
7cc55ed
Merge branch 'master' into prefer-ternary
fisker Sep 28, 2020
332ae21
Update prefer-ternary.md
sindresorhus Sep 29, 2020
cab9830
Update projects.js
sindresorhus Sep 29, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
235 changes: 235 additions & 0 deletions docs/rules/prefer-ternary.md
@@ -0,0 +1,235 @@
# Prefer ternary expressions over simple `if-else` statements

This rule enforces the use of ternary expressions over 'simple' `if-else` statements where 'simple' means the consequent and alternate are each one line and have the same basic type and form.

Using an `if-else` statement typically results in more lines of code than a single lined ternary expression, which leads to an unnecessarily larger codebase that is more difficult to maintain.

Additionally, using an `if-else` statement can result in defining variables using `let` or `var` solely to be reassigned within the blocks. This leads to varaibles being unnecessarily mutable and prevents `prefer-const` from flagging the variable.

This rule is fixable.


## Fail

```js
let foo =''
if(bar){
foo = 3
}
else{
foo = 4
}
```

```js
if(bar){
return 3
}
else{
return 4
}
```

```js
if(bar){
throw Error(123)
}
else{
throw Error(456)
}
```

## Pass

```js
let foo = bar ? 3 : 4
```

```js
return bar ? 3 : 4
```


```js
let foo = ''
if(bar){
baz()
foo = 3
}
else{
foo = 4
}
```

```js
if(bar){
foo = 3
}
else{
return 4
}
```

```js
throw bar ? Error(123) : Error(456)
```

## Options

This rule can take the following options:
* An object with the following keys: 'assignment', 'return', 'call', 'throw', 'new', 'yield', 'await'
* The string 'always'

### assignment
The assignment option determines whether the rule will flag assignment expressions. It can take the following values: 'never', 'same', 'always'. Default value is 'same'.

**never**: the rule will not flag any assignment statements.
With `{assigment: 'never'}` the following would both NOT be flagged:
```js
let foo =''
if(bar){
foo = 3
}
else{
foo = 4
}
```

```js
let foo =''
if(bar){
foo = 3
}
else{
baz = 4
}
```

**same**: the rule will flag assignment statements assigning to the same variable.
With `{assigment: 'same'}` the following would be flagged:
```js
let foo =''
if(bar){
foo = 3
}
else{
foo = 4
}
```
With `{assigment: 'same'}` the following would NOT be flagged:
```js
let foo =''
if(bar){
foo = 3
}
else{
baz = 4
}
```


**always**: the rule will flag all assignment statements.
With `{assigment: 'always'}` the following would both be flagged:
```js
let foo =''
if(bar){
foo = 3
}
else{
foo = 4
}
```

```js
let foo =''
if(bar){
foo = 3
}
else{
baz = 4
}
```

### return
The return option determines whether the rule will flag return expressions. It can take a boolean. Default value is true.
With `{return: false}` the following would NOT be flagged:
```js
let foo =''
if(bar){
return 3
}
else{
return 4
}
```

### call
The call option determines whether the rule will flag call expressions. It can take a boolean. Default value is false.
With `{call: true}` the following would be flagged:
```js
if(bar){
foo()
}
else{
baz()
}
Copy link
Owner

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.

```

### throw
The throw option determines whether the rule will flag throw statements. It can take a boolean. Default value is true.
With `{thow: false}` the following would NOT be flagged:
```js
if(bar){
throw Error(123)
}
else{
throw Error(456)
}
Copy link
Owner

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.

Copy link
Collaborator

@fisker fisker Feb 17, 2020

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');
}

Copy link
Contributor Author

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?

Copy link
Owner

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.

```

### new
The new option determines whether the rule will flag new constructors. It can take a boolean. Default value is false.
With `{new: true}` the following would be flagged:
```js
if(bar){
new foo()
}
else{
new baz()
}
```

### yield
The yield option determines whether the rule will flag yield expressions. It can take a boolean. Default value is false.
With `{yield: true}` the following would be flagged:
```js
function* foo(index) {
while (index < 10) {
if(index < 3){
yield index++;
}
else{
yield index * 2
}
}
}
```

### await
The await option determines whether the rule will flag await expressions. It can take a boolean. Default value is false.
With `{await: true}` the following would be flagged:
```js
async () => {
if(a){
await foo();
}
else{
await bar();
}
}
```


### 'always'

Always prefer ternary to simple `if-else` statements. This option is equivalent to ```{assignment: 'always', return: true, call:true, throw: true, new: true, yield: true, await: true}```.
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -57,6 +57,7 @@ module.exports = {
'unicorn/prefer-spread': 'error',
'unicorn/prefer-starts-ends-with': 'error',
'unicorn/prefer-string-slice': 'error',
'unicorn/prefer-ternary': 'error',
'unicorn/prefer-text-content': 'error',
'unicorn/prefer-trim-start-end': 'error',
'unicorn/prefer-type-error': 'error',
Expand Down
2 changes: 2 additions & 0 deletions readme.md
Expand Up @@ -75,6 +75,7 @@ Configure it in `package.json`.
"unicorn/prefer-spread": "error",
"unicorn/prefer-starts-ends-with": "error",
"unicorn/prefer-string-slice": "error",
"unicorn/prefer-ternary": "off",
"unicorn/prefer-text-content": "error",
"unicorn/prefer-trim-start-end": "error",
"unicorn/prefer-type-error": "error",
Expand Down Expand Up @@ -128,6 +129,7 @@ Configure it in `package.json`.
- [prefer-spread](docs/rules/prefer-spread.md) - Prefer the spread operator over `Array.from()`. *(fixable)*
- [prefer-starts-ends-with](docs/rules/prefer-starts-ends-with.md) - Prefer `String#startsWith()` & `String#endsWith()` over more complex alternatives.
- [prefer-string-slice](docs/rules/prefer-string-slice.md) - Prefer `String#slice()` over `String#substr()` and `String#substring()`. *(partly fixable)*
- [prefer-ternary](docs/rules/prefer-ternary.md) - Prefer ternary expressions over simple `if-else` statements. *(fixable)*
- [prefer-text-content](docs/rules/prefer-text-content.md) - Prefer `.textContent` over `.innerText`. *(fixable)*
- [prefer-trim-start-end](docs/rules/prefer-trim-start-end.md) - Prefer `String#trimStart()` / `String#trimEnd()` over `String#trimLeft()` / `String#trimRight()`. *(fixable)*
- [prefer-type-error](docs/rules/prefer-type-error.md) - Enforce throwing `TypeError` in type checking conditions. *(fixable)*
Expand Down