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): add no-duplicate-type-constituents rule #5728

Conversation

sajikix
Copy link
Contributor

@sajikix sajikix commented Oct 3, 2022

PR Checklist

Overview

Add new rule : no-duplicate-type-union-intersection-members.

This rule disallows duplicate union or intersection type members. The autofixer will remove all duplicates, keeping the first one.

I implemented it so that only notational duplicates are checked.
It means that members with the same value but different names are not marked duplicates.

type A = { a: string; b: number };
type B = { a: string; b: number };

type AorB = A | B; // Type A and B are the same type, but are not considered duplicates.

In the case of object types, it judged as duplicates only when the order of key and value are exactly the same.

// ❌ berfore remove duplicates
type T =
  | { a: string; b: number }
  | { a: string; b: number } // considered duplicates with the first one. 
  | { b: number; a: string }; // not considered duplicates with the first one. 
// ↓ 
// ✅ after remove duplicates
type T =
  | { a: string; b: number }
  | { b: number; a: string };

What has considered a duplicate in object type is a matter of debate. I would love to hear the reviewers' opinions and would like to improve it with the reviewers' input.

@typescript-eslint
Copy link
Contributor

Thanks for the PR, @sajikix!

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 Oct 3, 2022

Deploy Preview for typescript-eslint ready!

Name Link
🔨 Latest commit abb92d8
🔍 Latest deploy log https://app.netlify.com/sites/typescript-eslint/deploys/641d34c527f83800089d8e9e
😎 Deploy Preview https://deploy-preview-5728--typescript-eslint.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

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.

A great start, thanks for sending this in! 💓

Left some requests for changes - lmk if any of that doesn't make sense!

@JoshuaKGoldberg JoshuaKGoldberg added the awaiting response Issues waiting for a reply from the OP or another party label Oct 3, 2022
@codecov
Copy link

codecov bot commented Oct 11, 2022

Codecov Report

Merging #5728 (abb92d8) into main (720e811) will increase coverage by 0.05%.
The diff coverage is 100.00%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5728      +/-   ##
==========================================
+ Coverage   87.24%   87.29%   +0.05%     
==========================================
  Files         383      384       +1     
  Lines       13080    13139      +59     
  Branches     3845     3859      +14     
==========================================
+ Hits        11411    11470      +59     
  Misses       1302     1302              
  Partials      367      367              
Flag Coverage Δ
unittest 87.29% <100.00%> (+0.05%) ⬆️

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

Impacted Files Coverage Δ
...plugin/src/rules/no-duplicate-type-constituents.ts 100.00% <100.00%> (ø)

@bradzacher bradzacher removed the awaiting response Issues waiting for a reply from the OP or another party label Oct 15, 2022
@sajikix
Copy link
Contributor Author

sajikix commented Oct 16, 2022

Sorry for the wait since I got your review!

There have been two major changes since previous review.

  1. change duplicate check to recursive ASTNode comparison instead of string comparison from sourceCode.getText.
  2. use removeRange instead of replaceText in the fix function.

1. use recursive ASTNode comparison

As you reviewed, the previous implementation compared pure code strings, so there was a bug that prevented comparison if there were comments, etc. in the union/intersction type.
To resolve this, I changed to compare ASTNode recursively. Specifically, this rule is looking for an exact match for each ASTNode excluding range, loc, and parent properties. (I first tried to write a comparison process for each AstTypeNode, but there are so many cases that I am not confident I could cover them... 😭 )
I believe this method can be used to compare union/intersection members, but do you have any concerns?

2. use removeRange()

As you reviewed, we are using removeRange instead of replaceText in the fix function. Specifically, I set the removeRange as follows.

type T = A | A | B;
//        ^   ^
//     start  end

// start : end loc of the member before the duplicated member
// end : end loc of the duplicated member

// ↓ Deleting this range will result in ....
type T = A | B;

This is a relatively large change, but I would appreciate your review. 🙏

@JoshuaKGoldberg JoshuaKGoldberg changed the title feat(eslint-plugin): add no-duplicate-type-union-intersection-members rule feat(eslint-plugin): add no-duplicate-type-constituents rule Oct 17, 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.

Progress! I think this is getting closer - but nailing down edge cases (i.e. switching to === type equality) is going to be key.

@bradzacher bradzacher added enhancement: new plugin rule New rule request for eslint-plugin awaiting response Issues waiting for a reply from the OP or another party labels Oct 24, 2022
@sajikix
Copy link
Contributor Author

sajikix commented Mar 7, 2023

Thank you so much for your detailed explanation!
And I finally realized the problem with my implementation. 🙏

The problem with the current behavior is that there are multiple fixers (number of detected duplicates) that remove the exact same duplicate (the one at the end), right?
And this kind of fixer behavior is not very good for eslint's autofix behavior: .... 😭

Now that I am aware of the problem, I would like to implement it according to the second solution you suggested (= report / fixer only the first duplicates found in constituents).
In addition, I will include a change to correctly specify the node and loc options in the report function. This will make it easier to see which parts are duplicates and which parts will be removed. (You suggested this too!).

However, I am wondering if the above change (pointing out the first detected duplicate) should be applied to the report function as well, or only to the fixer.
In other words, which of the following is the preferred behavior?

  1. report and provide fixer only the first duplicate detected
type F = (A | B) | (A | B) | ((C | D) & (A | B)) | (A | B);
// reported         ^^^^^
// fixer            ^^^^^
  1. provide a fixer only for the first duplicate detected (report all duplicated)
type F = (A | B) | (A | B) | (C | D) & (A | B) | (A | B);
// reported         ^^^^^     ^^^^^^^^^^^^^^^     ^^^^^
// fixer            ^^^^^

@bradzacher bradzacher removed the awaiting response Issues waiting for a reply from the OP or another party label Mar 23, 2023
@sajikix
Copy link
Contributor Author

sajikix commented Mar 23, 2023

I have addressed the issues regarding the range of the fixer function and the clarity of the error reporting scope!

You can see these improvements in action by checking out the playground. (It should be fixed properly 🙏 )

Talking about implementation, I have cut the report function and changed the implementation to call it each time a duplicate is found.

armano2
armano2 previously approved these changes Mar 23, 2023
Copy link
Member

@armano2 armano2 left a comment

Choose a reason for hiding this comment

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

this look great, thank you for your hard work

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.

This is looking really great, nice work @sajikix! 👏

Requesting changes on performance tuning, non-generated tests, and some small touchups. Awesome! Looking forward this getting shipped!

@JoshuaKGoldberg JoshuaKGoldberg added the awaiting response Issues waiting for a reply from the OP or another party label Mar 23, 2023
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.

🔥 this is wonderful, thanks a million @sajikix! Really excited to get this out in front of users. Really appreciate you working with us on it!

I'll also modify #6014 to indicate we'll add this to the recommended-type-checked ruleset. Because IMO it should be a recommended rule. 😁

@sajikix
Copy link
Contributor Author

sajikix commented Mar 24, 2023

Thank you again and again for your kind review! 🙏
And we are very happy that you will be adding it to the ruleset for v6! 😁

Do I need to do any additional work on this PR? (Is it OK if I leave the rest to you?)

@JoshuaKGoldberg JoshuaKGoldberg merged commit bc31078 into typescript-eslint:main Mar 24, 2023
37 of 38 checks passed
@JoshuaKGoldberg
Copy link
Member

none needed, was just waiting for the build to pass then got distracted 😄

crapStone pushed a commit to Calciumdibromid/CaBr2 that referenced this pull request Mar 28, 2023
This PR contains the following updates:

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

---

### Release Notes

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

### [`v5.57.0`](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#&#8203;5570-httpsgithubcomtypescript-eslinttypescript-eslintcomparev5560v5570-2023-03-27)

[Compare Source](typescript-eslint/typescript-eslint@v5.56.0...v5.57.0)

##### Bug Fixes

-   **eslint-plugin:** \[no-unnecessary-boolean-literal-compare] simplify fixer and add support for double negation ([#&#8203;6620](typescript-eslint/typescript-eslint#6620)) ([81c8519](typescript-eslint/typescript-eslint@81c8519))
-   **eslint-plugin:** correct crashes with getTypeArguments for ts < 3.7 ([#&#8203;6767](typescript-eslint/typescript-eslint#6767)) ([59eab58](typescript-eslint/typescript-eslint@59eab58))

##### Features

-   **eslint-plugin:** \[consistent-type-assertions] add suggestions for objectLiteralTypeAssertions ([#&#8203;6642](typescript-eslint/typescript-eslint#6642)) ([720e811](typescript-eslint/typescript-eslint@720e811))
-   **eslint-plugin:** \[consistent-type-assertions] autofix angle bracket assertions to as ([#&#8203;6641](typescript-eslint/typescript-eslint#6641)) ([ad8ea64](typescript-eslint/typescript-eslint@ad8ea64))
-   **eslint-plugin:** add `no-duplicate-type-constituents` rule ([#&#8203;5728](typescript-eslint/typescript-eslint#5728)) ([bc31078](typescript-eslint/typescript-eslint@bc31078))

</details>

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

### [`v5.57.0`](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#&#8203;5570-httpsgithubcomtypescript-eslinttypescript-eslintcomparev5560v5570-2023-03-27)

[Compare Source](typescript-eslint/typescript-eslint@v5.56.0...v5.57.0)

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

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - 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, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNS4yNC4yIiwidXBkYXRlZEluVmVyIjoiMzUuMjQuMiJ9-->

Co-authored-by: cabr2-bot <cabr2.help@gmail.com>
Reviewed-on: https://codeberg.org/Calciumdibromid/CaBr2/pulls/1831
Reviewed-by: Epsilon_02 <epsilon_02@noreply.codeberg.org>
Co-authored-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
Co-committed-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
awaiting response Issues waiting for a reply from the OP or another party enhancement: new plugin rule New rule request for eslint-plugin
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rule proposal: disallow duplicate union and intersection types
5 participants