Skip to content

fix(eslint-plugin): [no-unnecessary-condition] handle left-hand optional with exactOptionalPropertyTypes option #8249

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

Merged
merged 2 commits into from
Feb 1, 2024

Conversation

yeonjuan
Copy link
Contributor

PR Checklist

Overview

This PR fixes a false positive issue that occurs when using the exactOptionalPropertyTypes option.

Playground

tsconfig.json

{
  "compilerOptions": {
    "exactOptionalPropertyTypes": true,
    "strictNullChecks": true
  }
}

code.ts

declare const foo: { bar?: number };
foo.bar ??= 1;

When the exactOptionalPropertyTypes option is true, typescript removes undefined type of the expression on the left-hand side in assignment. This seems to be the way typescript is intended to provide the functionality of exactOptionalPropertyTypes.

Playground

declare const foo: { bar?: number };
foo.bar ?? 1; // type of 'foo.bar' is `number | undefined`
foo.bar ??= 1; // type of 'foo.bar' is `number`

In this PR, I added code to get the property's Symbol from the member expression's object type and check if whether it's optional.

This implementation has a limitation in that it cannot handle well for using computed key. If it should be handled, I'll need to modify the code a bit more, and I'd love to hear feedback.

 declare const foo: { bar: { baz?: number }; };
 declare const baz: 'baz';
 foo.bar[baz] ??= 1;

@typescript-eslint
Copy link
Contributor

Thanks for the PR, @yeonjuan!

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.

Copy link

netlify bot commented Jan 14, 2024

Deploy Preview for typescript-eslint ready!

Name Link
🔨 Latest commit ad3769f
🔍 Latest deploy log https://app.netlify.com/sites/typescript-eslint/deploys/65bbc41b1f9928000750fc34
😎 Deploy Preview https://deploy-preview-8249--typescript-eslint.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 99 (🟢 up 9 from production)
Accessibility: 100 (no change from production)
Best Practices: 92 (no change from production)
SEO: 98 (no change from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports

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

@yeonjuan yeonjuan force-pushed the fix/6635 branch 4 times, most recently from f4a4bfc to 00ff844 Compare January 25, 2024 14:54
@yeonjuan yeonjuan force-pushed the fix/6635 branch 2 times, most recently from fcfa73f to f2145b1 Compare January 28, 2024 04:09
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.

The functionality & tests look good to me, thanks!

Just requesting changes on handling computed values. LMK if the prompt I gave isn't enough info to go off of?

Comment on lines 208 to 213
// TODO - figure out how to handle when computed key used.
/*
declare const foo: { bar: { baz?: number }; };
declare const baz: 'baz';
foo.bar[baz] ??= 1;
*/
Copy link
Member

Choose a reason for hiding this comment

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

[Feature] Yeah we'll have to do something here to make this PR mergable.

Have you tried getStaticValue? That's kind of the standard utility we've been going with.

For really complex cases where folks are doing funky stuff like (1 * 1) + "other" then it's fine to not handle them.

Copy link
Contributor Author

@yeonjuan yeonjuan Feb 1, 2024

Choose a reason for hiding this comment

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

Hi @JoshuaKGoldberg, While looking into handling the computed key case, I found another false positive which occurs regardless of the exactOptionalPropertyTypes.

enum Keys {
  A = 'A',
  B = 'B',
}
type Foo = {
  [Keys.A]: number | null;
  [Keys.B]: number;
};
declare const foo: Foo;
declare const key: Keys;
foo[key] ??= 1; // Unnecessary conditional, expected left-hand side of `??` operator to be possibly null or undefined. 11:1 - 11:9

let bar = foo[key];
bar ??= 1; // No Error

I think this false positive and the #6635 can be handled together, without branching based on exactOptionalPropertyTypes.

I'd like to come back to it after it's been triaged, what do you think about this false positive?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah that makes sense to me. Nice find!

@JoshuaKGoldberg JoshuaKGoldberg added the awaiting response Issues waiting for a reply from the OP or another party label Jan 30, 2024
Copy link

codecov bot commented Jan 31, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (8de6ee2) 87.70% compared to head (ad3769f) 87.71%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8249      +/-   ##
==========================================
+ Coverage   87.70%   87.71%   +0.01%     
==========================================
  Files         397      397              
  Lines       13950    13963      +13     
  Branches     4055     4061       +6     
==========================================
+ Hits        12235    12248      +13     
  Misses       1403     1403              
  Partials      312      312              
Flag Coverage Δ
unittest 87.71% <100.00%> (+0.01%) ⬆️

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

Files Coverage Δ
...slint-plugin/src/rules/no-unnecessary-condition.ts 98.56% <100.00%> (+0.09%) ⬆️

@github-actions github-actions bot removed the awaiting response Issues waiting for a reply from the OP or another party label Feb 1, 2024
JoshuaKGoldberg
JoshuaKGoldberg previously approved these changes Feb 1, 2024
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 looks great - some tricky logic to handle, nice job getting it working! 👏

Just one typo in a function name. I'll go ahead and apply it myself to get this merged & unblock followups.

@JoshuaKGoldberg JoshuaKGoldberg added the 1 approval >=1 team member has approved this PR; we're now leaving it open for more reviews before we merge label Feb 1, 2024
@JoshuaKGoldberg JoshuaKGoldberg merged commit 8e3609b into typescript-eslint:main Feb 1, 2024
danvk pushed a commit to danvk/typescript-eslint that referenced this pull request Feb 4, 2024
…nal with exactOptionalPropertyTypes option (typescript-eslint#8249)

* fix(eslint-plugin): [no-unnecessary-condition] handle left-hand optional with exactOptionalPropertyTypes option

* typo: remove the 's'

---------

Co-authored-by: Josh Goldberg <git@joshuakgoldberg.com>
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 9, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
1 approval >=1 team member has approved this PR; we're now leaving it open for more reviews before we merge
Projects
None yet
2 participants