-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
fix(eslint-plugin): [no-unnecessary-condition] false positives with branded types #7466
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,9 @@ const isTruthyLiteral = (type: ts.Type): boolean => | |
const isPossiblyFalsy = (type: ts.Type): boolean => | ||
tsutils | ||
.unionTypeParts(type) | ||
// Intersections like `string & {}` can also be possibly falsy, | ||
// requiring us to look into the intersection. | ||
.flatMap(type => tsutils.intersectionTypeParts(type)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [External] Heh, I wonder if |
||
// PossiblyFalsy flag includes literal values, so exclude ones that | ||
// are definitely truthy | ||
.filter(t => !isTruthyLiteral(t)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -83,6 +83,11 @@ const result2 = foo() == null; | |
necessaryConditionTest('null | object'), | ||
necessaryConditionTest('undefined | true'), | ||
necessaryConditionTest('void | true'), | ||
// "branded" type | ||
necessaryConditionTest('string & {}'), | ||
necessaryConditionTest('string & { __brand: string }'), | ||
necessaryConditionTest('number & {}'), | ||
necessaryConditionTest('boolean & {}'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Testing] Normally we'll want both Additionally, some maybe missing areas:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the feedback! 😃 I have added more tests, especially for more complex scenarios that looked like potential edge cases to me. While adding tests for Two points regarding your comments:
I think so too 👍
I believe that both should behave just like if you wrote Btw: I tried to find a scenario where we need to recursively unwrap unions/intersections. Something like |
||
|
||
necessaryConditionTest('any'), // any | ||
necessaryConditionTest('unknown'), // unknown | ||
|
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 works because:
intersectionTypeParts(type)
just returns the original type, making the function behave like beforestring & {}
,intersectionTypeParts(type)
returns an array containing the typesstring
and{}
.string
in this example is possibly falsy, making our function correctly report that the entire thing is possibly falsystring & number
, the type checker directly reports this type asnever
, i.e. here we shouldn't even see that this was ever defined as an intersection in the first place