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

feat(eslint-plugin): [prefer-optional-chain] support logical with empty object #4430

Conversation

omril1
Copy link
Contributor

@omril1 omril1 commented Jan 10, 2022

Adds empty object pattern ((foo || {}).bar or (foo ?? {}).bar) as a match for prefer-optional-chain rule

PR Checklist

Overview

Adds support for suggesting optional chaining refactor when an empty object is used to mimic optional chaining in addition to the existing && pattern ((foo || {}).bar and (foo ?? {}).bar are roughly equivalent to foo && foo.bar or foo?.bar)

Notes

  • The feature does not uses type information
  • The feature does not handle function calls in this way as (foo || {}).bar() can cause a runtime error (unlike the match for &&).

Empty object as optional chaining
@nx-cloud
Copy link

nx-cloud bot commented Jan 10, 2022

☁️ Nx Cloud Report

CI ran the following commands for commit 44236bb. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this branch


✅ Successfully ran 43 targets

Sent with 💌 from NxCloud.

@typescript-eslint
Copy link
Contributor

Thanks for the PR, @omril1!

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.

@netlify
Copy link

netlify bot commented Jan 10, 2022

✅ Deploy Preview for typescript-eslint ready!

🔨 Explore the source changes: 44236bb

🔍 Inspect the deploy log: https://app.netlify.com/sites/typescript-eslint/deploys/6234ebec3a1fff0008fe7c9d

😎 Browse the preview: https://deploy-preview-4430--typescript-eslint.netlify.app

@codecov
Copy link

codecov bot commented Jan 10, 2022

Codecov Report

Merging #4430 (44236bb) into main (8ec05be) will increase coverage by 0.05%.
The diff coverage is 95.23%.

@@            Coverage Diff             @@
##             main    #4430      +/-   ##
==========================================
+ Coverage   94.25%   94.30%   +0.05%     
==========================================
  Files         151      151              
  Lines        7971     7991      +20     
  Branches     2573     2578       +5     
==========================================
+ Hits         7513     7536      +23     
+ Misses        262      258       -4     
- Partials      196      197       +1     
Flag Coverage Δ
unittest 94.30% <95.23%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
...s/eslint-plugin/src/rules/prefer-optional-chain.ts 94.11% <95.23%> (+0.17%) ⬆️
...es/eslint-plugin/src/util/getOperatorPrecedence.ts 42.85% <0.00%> (+11.42%) ⬆️

@bradzacher bradzacher changed the title feat(eslint-plugin): Issue 4395 - Empty object as match for optional chaining rule feat(eslint-plugin): [prefer-optional-chain] support logical with empty object Jan 11, 2022
@bradzacher bradzacher added the enhancement New feature or request label Jan 11, 2022
@omril1 omril1 marked this pull request as ready for review January 11, 2022 10:23
Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great so far, thanks!

Just requesting changes on a bit more testing please ❤️

@@ -48,6 +48,48 @@ export default util.createRule({
create(context) {
const sourceCode = context.getSourceCode();
return {
'LogicalExpression[operator=||]'(node: TSESTree.LogicalExpression): void {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feature does not handle function calls in this way as (foo || {}).bar() can cause a runtime error.

Hmm, interesting point. My hunch would be that we probably want to handle function calls for cases like this:

(someContainer || {}).hasOwnProperty("someKey")

...but I wouldn't consider it a blocker / must-have for this PR. Up to you!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JoshuaKGoldberg Care to re-review this? I don't think I'm going to add object built-ins in the PR

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You got it, no problem!

@JoshuaKGoldberg JoshuaKGoldberg added the awaiting response Issues waiting for a reply from the OP or another party label Jan 24, 2022
JoshuaKGoldberg
JoshuaKGoldberg previously approved these changes Feb 3, 2022
Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me, thanks so much for the thorough implementation & discussion! 🎉

@bradzacher is all over the history for this already-complex file so I'll just want to get a second opinion before merging.

Co-authored-by: Josh Goldberg <me@joshuakgoldberg.com>
@bradzacher bradzacher removed the awaiting response Issues waiting for a reply from the OP or another party label Feb 9, 2022
@bradzacher bradzacher added the awaiting response Issues waiting for a reply from the OP or another party label Feb 9, 2022
@bradzacher bradzacher removed the awaiting response Issues waiting for a reply from the OP or another party label Feb 14, 2022
@omril1
Copy link
Contributor Author

omril1 commented Feb 23, 2022

@bradzacher & @JoshuaKGoldberg any update about this PR?

@bradzacher
Copy link
Member

it's on the review queue for when the volunteer maintainers have time to review PRs!

Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good! Just one comment

Comment on lines 76 to 83
// Any node that is made of an operator with higher or equal precedence,
const maybeWrappedLeftNode =
leftNode.type === AST_NODE_TYPES.BinaryExpression ||
leftNode.type === AST_NODE_TYPES.TSAsExpression ||
leftNode.type === AST_NODE_TYPES.UnaryExpression ||
leftNode.type === AST_NODE_TYPES.LogicalExpression ||
leftNode.type === AST_NODE_TYPES.ConditionalExpression ||
leftNode.type === AST_NODE_TYPES.AwaitExpression
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a module which supports getting the precedence!
We should use this so that we handle all of the cases properly, considering it's a fixer.

It's a tiny bit cumbersome because we need to get the ts node first.

const parserServices = util.getParserServices(context, true);

// ...

function isHigherPrecedence() {
  const logicalTsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
  const logicalPrecedence = util.getOperatorPrecedence(node.kind, node.operatorToken.kind);

  const leftTsNode = parserServices.esTreeNodeToTSNodeMap.get(leftNode);
  const operator = isBinaryExpression(node)
    ? node.operatorToken.kind
    : ts.SyntaxKind.Unknown;
  const leftPrecedence = util.getOperatorPrecedence(leftTsNode.kind, operator);

  return leftPrecedence > logicalPrecedence;
}

(Eventually we should make this API work with ESTree nodes so it's nicer)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I did that with a slight alteration because it didn't work (even after fixing the variable names from the snippet)

@bradzacher bradzacher added the awaiting response Issues waiting for a reply from the OP or another party label Mar 18, 2022
@omril1 omril1 requested a review from bradzacher March 18, 2022 20:33
Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - thanks for thsi!

@bradzacher bradzacher removed the awaiting response Issues waiting for a reply from the OP or another party label Mar 18, 2022
@bradzacher bradzacher merged commit d21cfe0 into typescript-eslint:main Mar 18, 2022
crapStone pushed a commit to Calciumdibromid/CaBr2 that referenced this pull request Mar 23, 2022
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint) | devDependencies | minor | [`5.15.0` -> `5.16.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2feslint-plugin/5.15.0/5.16.0) |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint) | devDependencies | minor | [`5.15.0` -> `5.16.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2fparser/5.15.0/5.16.0) |

---

### Release Notes

<details>
<summary>typescript-eslint/typescript-eslint (@&#8203;typescript-eslint/eslint-plugin)</summary>

### [`v5.16.0`](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#&#8203;5160-httpsgithubcomtypescript-eslinttypescript-eslintcomparev5150v5160-2022-03-21)

[Compare Source](typescript-eslint/typescript-eslint@v5.15.0...v5.16.0)

##### Bug Fixes

-   **eslint-plugin:** \[consistent-type-assertions] enforce assertionStyle for `const` assertions ([#&#8203;4685](typescript-eslint/typescript-eslint#4685)) ([8ec05be](typescript-eslint/typescript-eslint@8ec05be))

##### Features

-   **eslint-plugin:** \[prefer-optional-chain] support logical with empty object ([#&#8203;4430](typescript-eslint/typescript-eslint#4430)) ([d21cfe0](typescript-eslint/typescript-eslint@d21cfe0))

</details>

<details>
<summary>typescript-eslint/typescript-eslint (@&#8203;typescript-eslint/parser)</summary>

### [`v5.16.0`](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#&#8203;5160-httpsgithubcomtypescript-eslinttypescript-eslintcomparev5150v5160-2022-03-21)

[Compare Source](typescript-eslint/typescript-eslint@v5.15.0...v5.16.0)

**Note:** Version bump only for package [@&#8203;typescript-eslint/parser](https://github.com/typescript-eslint/parser)

</details>

---

### Configuration

📅 **Schedule**: At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox.

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).

Co-authored-by: cabr2-bot <cabr2.help@gmail.com>
Reviewed-on: https://codeberg.org/Calciumdibromid/CaBr2/pulls/1236
Reviewed-by: 6543 <6543@noreply.codeberg.org>
Co-authored-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
Co-committed-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
@avaly
Copy link
Contributor

avaly commented Mar 28, 2022

This change seems to be a breaking change because this rule now requires type information.

Error: Error while loading rule '@typescript-eslint/prefer-optional-chain': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.

@bradzacher
Copy link
Member

bradzacher commented Mar 28, 2022

@avaly As per our contributing guide. Please don't comment on closed PRs.
Instead raise an issue and fill out the template.

@typescript-eslint typescript-eslint locked and limited conversation to collaborators Mar 28, 2022
@omril1 omril1 deleted the feat/issue-4395-empty-object-optional-chaining branch June 4, 2022 08:17
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[prefer-optional-chain] Suggest (foo || {}).bar as a valid match for the rule
5 participants