Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dba0bed

Browse files
fix(eslint-plugin): [prefer-nullish-coalescing] treat any/unknown as eligible for nullish coalescing (typescript-eslint#10865)
* treat any/unknown as eligible for nullish coalescing * more stuff * fix typo Co-authored-by: Ronen Amiel <[email protected]> * feedback * tweaks * merge mistake * rename vars in test Co-authored-by: Ronen Amiel <[email protected]> --------- Co-authored-by: Ronen Amiel <[email protected]>
1 parent 7f2a5bd commit dba0bed

File tree

7 files changed

+216
-93
lines changed

7 files changed

+216
-93
lines changed

packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Examples of code for this rule with `{ ignoreConditionalTests: false }`:
8484
<TabItem value="❌ Incorrect">
8585

8686
```ts option='{ "ignoreConditionalTests": false }'
87-
declare const a: string | null;
87+
declare let a: string | null;
8888
declare const b: string | null;
8989

9090
if (a || b) {
@@ -102,7 +102,7 @@ a || b ? true : false;
102102
<TabItem value="✅ Correct">
103103

104104
```ts option='{ "ignoreConditionalTests": false }'
105-
declare const a: string | null;
105+
declare let a: string | null;
106106
declare const b: string | null;
107107

108108
if (a ?? b) {
@@ -133,7 +133,7 @@ Examples of code for this rule with `{ ignoreMixedLogicalExpressions: false }`:
133133
<TabItem value="❌ Incorrect">
134134

135135
```ts option='{ "ignoreMixedLogicalExpressions": false }'
136-
declare const a: string | null;
136+
declare let a: string | null;
137137
declare const b: string | null;
138138
declare const c: string | null;
139139
declare const d: string | null;
@@ -149,7 +149,7 @@ a || (b && c && d);
149149
<TabItem value="✅ Correct">
150150

151151
```ts option='{ "ignoreMixedLogicalExpressions": false }'
152-
declare const a: string | null;
152+
declare let a: string | null;
153153
declare const b: string | null;
154154
declare const c: string | null;
155155
declare const d: string | null;

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@ import {
1212
getTypeName,
1313
getTypeOfPropertyOfName,
1414
getValueOfLiteralType,
15-
isAlwaysNullish,
1615
isArrayMethodCallWithPredicate,
1716
isIdentifier,
1817
isNullableType,
1918
isPossiblyFalsy,
20-
isPossiblyNullish,
2119
isPossiblyTruthy,
2220
isTypeAnyType,
2321
isTypeFlagSet,
@@ -31,6 +29,25 @@ import {
3129
} from '../util/assertionFunctionUtils';
3230

3331
// #region
32+
33+
const nullishFlag = ts.TypeFlags.Undefined | ts.TypeFlags.Null;
34+
35+
function isNullishType(type: ts.Type): boolean {
36+
return tsutils.isTypeFlagSet(type, nullishFlag);
37+
}
38+
39+
function isAlwaysNullish(type: ts.Type): boolean {
40+
return tsutils.unionTypeParts(type).every(isNullishType);
41+
}
42+
43+
/**
44+
* Note that this differs from {@link isNullableType} in that it doesn't consider
45+
* `any` or `unknown` to be nullable.
46+
*/
47+
function isPossiblyNullish(type: ts.Type): boolean {
48+
return tsutils.unionTypeParts(type).some(isNullishType);
49+
}
50+
3451
function toStaticValue(
3552
type: ts.Type,
3653
):

packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
isNodeEqual,
1414
isNodeOfTypes,
1515
isNullLiteral,
16-
isPossiblyNullish,
16+
isNullableType,
1717
isUndefinedIdentifier,
1818
nullThrows,
1919
NullThrowsReasons,
@@ -193,7 +193,7 @@ export default createRule<Options, MessageIds>({
193193
* a nullishness check, taking into account the rule's configuration.
194194
*/
195195
function isTypeEligibleForPreferNullish(type: ts.Type): boolean {
196-
if (!isPossiblyNullish(type)) {
196+
if (!isNullableType(type)) {
197197
return false;
198198
}
199199

@@ -211,14 +211,33 @@ export default createRule<Options, MessageIds>({
211211
]
212212
.filter((flag): flag is number => typeof flag === 'number')
213213
.reduce((previous, flag) => previous | flag, 0);
214+
215+
if (ignorableFlags === 0) {
216+
// any types are eligible for conversion.
217+
return true;
218+
}
219+
220+
// if the type is `any` or `unknown` we can't make any assumptions
221+
// about the value, so it could be any primitive, even though the flags
222+
// won't be set.
223+
//
224+
// technically, this is true of `void` as well, however, it's a TS error
225+
// to test `void` for truthiness, so we don't need to bother checking for
226+
// it in valid code.
227+
if (
228+
tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)
229+
) {
230+
return false;
231+
}
232+
214233
if (
215-
type.flags !== ts.TypeFlags.Null &&
216-
type.flags !== ts.TypeFlags.Undefined &&
217-
(type as ts.UnionOrIntersectionType).types.some(t =>
218-
tsutils
219-
.intersectionTypeParts(t)
220-
.some(t => tsutils.isTypeFlagSet(t, ignorableFlags)),
221-
)
234+
tsutils
235+
.typeParts(type)
236+
.some(t =>
237+
tsutils
238+
.intersectionTypeParts(t)
239+
.some(t => tsutils.isTypeFlagSet(t, ignorableFlags)),
240+
)
222241
) {
223242
return false;
224243
}

packages/eslint-plugin/src/util/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export * from './getConstraintInfo';
2828
export * from './getValueOfLiteralType';
2929
export * from './isHigherPrecedenceThanAwait';
3030
export * from './skipChainExpression';
31-
export * from './truthinessAndNullishUtils';
31+
export * from './truthinessUtils';
3232

3333
// this is done for convenience - saves migrating all of the old rules
3434
export * from '@typescript-eslint/type-utils';

packages/eslint-plugin/src/util/truthinessAndNullishUtils.ts renamed to packages/eslint-plugin/src/util/truthinessUtils.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,3 @@ export const isPossiblyTruthy = (type: ts.Type): boolean =>
2828
// like `"" & { __brand: string }`.
2929
intersectionParts.every(type => !tsutils.isFalsyType(type)),
3030
);
31-
32-
// Nullish utilities
33-
const nullishFlag = ts.TypeFlags.Undefined | ts.TypeFlags.Null;
34-
const isNullishType = (type: ts.Type): boolean =>
35-
tsutils.isTypeFlagSet(type, nullishFlag);
36-
37-
export const isPossiblyNullish = (type: ts.Type): boolean =>
38-
tsutils.unionTypeParts(type).some(isNullishType);
39-
40-
export const isAlwaysNullish = (type: ts.Type): boolean =>
41-
tsutils.unionTypeParts(type).every(isNullishType);

packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-nullish-coalescing.shot

Lines changed: 7 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)