Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit22f7f25

Browse files
fix(eslint-plugin): [no-unnecessary-condition] improve error message for literal comparisons (typescript-eslint#10194)
* no-unnecessary-condition: Improve error message for literal comparisons* lintfix* cov* ts-api-utils bug workaround* cov* update todo with ts-api-utils issue* merge cleanup
1 parente2e9ffc commit22f7f25

File tree

2 files changed

+449
-168
lines changed

2 files changed

+449
-168
lines changed

‎packages/eslint-plugin/src/rules/no-unnecessary-condition.ts

Lines changed: 103 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ const valueIsPseudoBigInt = (
3232
returntypeofvalue==='object';
3333
};
3434

35-
constgetValue=(type:ts.LiteralType):bigint|number|string=>{
35+
constgetValueOfLiteralType=(
36+
type:ts.LiteralType,
37+
):bigint|number|string=>{
3638
if(valueIsPseudoBigInt(type.value)){
37-
returnBigInt((type.value.negative ?'-' :'')+type.value.base10Value);
39+
returnpseudoBigIntToBigInt(type.value);
3840
}
3941
returntype.value;
4042
};
@@ -43,13 +45,12 @@ const isFalsyBigInt = (type: ts.Type): boolean => {
4345
return(
4446
tsutils.isLiteralType(type)&&
4547
valueIsPseudoBigInt(type.value)&&
46-
!getValue(type)
48+
!getValueOfLiteralType(type)
4749
);
4850
};
4951
constisTruthyLiteral=(type:ts.Type):boolean=>
5052
tsutils.isTrueLiteralType(type)||
51-
// || type.
52-
(type.isLiteral()&&!!getValue(type));
53+
(type.isLiteral()&&!!getValueOfLiteralType(type));
5354

5455
constisPossiblyFalsy=(type:ts.Type):boolean=>
5556
tsutils
@@ -89,13 +90,83 @@ const isPossiblyNullish = (type: ts.Type): boolean =>
8990
constisAlwaysNullish=(type:ts.Type):boolean=>
9091
tsutils.unionTypeParts(type).every(isNullishType);
9192

92-
// isLiteralType only covers numbers and strings, this is a more exhaustive check.
93-
constisLiteral=(type:ts.Type):boolean=>
94-
tsutils.isBooleanLiteralType(type)||
95-
type.flags===ts.TypeFlags.Undefined||
96-
type.flags===ts.TypeFlags.Null||
97-
type.flags===ts.TypeFlags.Void||
98-
type.isLiteral();
93+
functiontoStaticValue(
94+
type:ts.Type,
95+
):
96+
|{value:bigint|boolean|number|string|null|undefined}
97+
|undefined{
98+
// type.isLiteral() only covers numbers/bigints and strings, hence the rest of the branches.
99+
if(tsutils.isBooleanLiteralType(type)){
100+
// Using `type.intrinsicName` instead of `type.value` because `type.value`
101+
// is `undefined`, contrary to what the type guard tells us.
102+
// See https://github.com/JoshuaKGoldberg/ts-api-utils/issues/528
103+
return{value:type.intrinsicName==='true'};
104+
}
105+
if(type.flags===ts.TypeFlags.Undefined){
106+
return{value:undefined};
107+
}
108+
if(type.flags===ts.TypeFlags.Null){
109+
return{value:null};
110+
}
111+
if(type.isLiteral()){
112+
return{value:getValueOfLiteralType(type)};
113+
}
114+
115+
returnundefined;
116+
}
117+
118+
functionpseudoBigIntToBigInt(value:ts.PseudoBigInt):bigint{
119+
returnBigInt((value.negative ?'-' :'')+value.base10Value);
120+
}
121+
122+
constBOOL_OPERATORS=newSet([
123+
'<',
124+
'>',
125+
'<=',
126+
'>=',
127+
'==',
128+
'===',
129+
'!=',
130+
'!==',
131+
]asconst);
132+
133+
typeBoolOperator=typeofBOOL_OPERATORSextendsSet<inferT> ?T :never;
134+
135+
functionisBoolOperator(operator:string):operator isBoolOperator{
136+
return(BOOL_OPERATORSasSet<string>).has(operator);
137+
}
138+
139+
functionbooleanComparison(
140+
left:unknown,
141+
operator:BoolOperator,
142+
right:unknown,
143+
):boolean{
144+
switch(operator){
145+
case'!=':
146+
// eslint-disable-next-line eqeqeq -- intentionally comparing with loose equality
147+
returnleft!=right;
148+
case'!==':
149+
returnleft!==right;
150+
case'<':
151+
//@ts-expect-error: we don't care if the comparison seems unintentional.
152+
returnleft<right;
153+
case'<=':
154+
//@ts-expect-error: we don't care if the comparison seems unintentional.
155+
returnleft<=right;
156+
case'==':
157+
// eslint-disable-next-line eqeqeq -- intentionally comparing with loose equality
158+
returnleft==right;
159+
case'===':
160+
returnleft===right;
161+
case'>':
162+
//@ts-expect-error: we don't care if the comparison seems unintentional.
163+
returnleft>right;
164+
case'>=':
165+
//@ts-expect-error: we don't care if the comparison seems unintentional.
166+
returnleft>=right;
167+
}
168+
}
169+
99170
// #endregion
100171

101172
exporttypeOptions=[
@@ -141,7 +212,7 @@ export default createRule<Options, MessageId>({
141212
alwaysTruthyFunc:
142213
'This callback should return a conditional, but return is always truthy.',
143214
literalBooleanExpression:
144-
'Unnecessary conditional,bothsides of theexpression areliteralvalues.',
215+
'Unnecessary conditional,comparison is always {{trueOrFalse}}. Bothsides of thecomparison always have aliteraltype.',
145216
never:'Unnecessary conditional, value is `never`.',
146217
neverNullish:
147218
'Unnecessary conditional, expected left-hand side of `??` operator to be possibly null or undefined.',
@@ -397,19 +468,6 @@ export default createRule<Options, MessageId>({
397468
* - https://github.com/microsoft/TypeScript/issues/32627
398469
* - https://github.com/microsoft/TypeScript/issues/37160 (handled)
399470
*/
400-
constBOOL_OPERATORS=newSet([
401-
'<',
402-
'>',
403-
'<=',
404-
'>=',
405-
'==',
406-
'===',
407-
'!=',
408-
'!==',
409-
]asconst);
410-
typeBoolOperator=Parameters<typeofBOOL_OPERATORS.has>[0];
411-
constisBoolOperator=(operator:string):operator isBoolOperator=>
412-
(BOOL_OPERATORSasSet<string>).has(operator);
413471
functioncheckIfBoolExpressionIsNecessaryConditional(
414472
node:TSESTree.Node,
415473
left:TSESTree.Node,
@@ -418,10 +476,27 @@ export default createRule<Options, MessageId>({
418476
):void{
419477
constleftType=getConstrainedTypeAtLocation(services,left);
420478
constrightType=getConstrainedTypeAtLocation(services,right);
421-
if(isLiteral(leftType)&&isLiteral(rightType)){
422-
context.report({ node,messageId:'literalBooleanExpression'});
479+
480+
constleftStaticValue=toStaticValue(leftType);
481+
constrightStaticValue=toStaticValue(rightType);
482+
483+
if(leftStaticValue!=null&&rightStaticValue!=null){
484+
constconditionIsTrue=booleanComparison(
485+
leftStaticValue.value,
486+
operator,
487+
rightStaticValue.value,
488+
);
489+
490+
context.report({
491+
node,
492+
messageId:'literalBooleanExpression',
493+
data:{
494+
trueOrFalse:conditionIsTrue ?'true' :'false',
495+
},
496+
});
423497
return;
424498
}
499+
425500
// Workaround for https://github.com/microsoft/TypeScript/issues/37160
426501
if(isStrictNullChecks){
427502
constUNDEFINED=ts.TypeFlags.Undefined;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp