eslint-plugin-th-rules 3.4.0 → 3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-explicit-nil-check.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-explicit-nil-check.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAWtF,QAAA,MAAM,sBAAsB;;
|
|
1
|
+
{"version":3,"file":"prefer-explicit-nil-check.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-explicit-nil-check.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAWtF,QAAA,MAAM,sBAAsB;;CA0d1B,CAAC;AAEH,eAAe,sBAAsB,CAAC"}
|
|
@@ -46,12 +46,33 @@ const preferExplicitNilCheck = createRule({
|
|
|
46
46
|
return node.expression;
|
|
47
47
|
return node;
|
|
48
48
|
}
|
|
49
|
+
function getTypeFromSymbolAnnotation(symbol) {
|
|
50
|
+
const decl = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
51
|
+
if (_.isNil(decl))
|
|
52
|
+
return null;
|
|
53
|
+
if ('type' in decl) {
|
|
54
|
+
const typeNode = decl.type;
|
|
55
|
+
if (!_.isNil(typeNode)) {
|
|
56
|
+
return checker.getTypeFromTypeNode(typeNode);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
49
61
|
function getTsType(node) {
|
|
50
62
|
const unwrapped = unwrapChainExpression(node);
|
|
51
63
|
const tsNode = services.esTreeNodeToTSNodeMap.get(unwrapped);
|
|
52
64
|
if (_.isNil(tsNode))
|
|
53
65
|
return null;
|
|
54
|
-
|
|
66
|
+
let type;
|
|
67
|
+
if (unwrapped.type === AST_NODE_TYPES.Identifier) {
|
|
68
|
+
const symbol = checker.getSymbolAtLocation(tsNode);
|
|
69
|
+
const annotated = _.isNil(symbol) ? null : getTypeFromSymbolAnnotation(symbol);
|
|
70
|
+
type = annotated ?? checker.getTypeAtLocation(tsNode);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
type = checker.getTypeAtLocation(tsNode);
|
|
74
|
+
}
|
|
75
|
+
return checker.getWidenedType(type);
|
|
55
76
|
}
|
|
56
77
|
function isNullableFlag(flags) {
|
|
57
78
|
return (flags & ts.TypeFlags.Null) !== 0 || (flags & ts.TypeFlags.Undefined) !== 0;
|
|
@@ -86,6 +107,38 @@ const preferExplicitNilCheck = createRule({
|
|
|
86
107
|
}
|
|
87
108
|
return false;
|
|
88
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Returns true when the type is a union that includes boolean-like AND also
|
|
112
|
+
* includes some other non-nullish, non-boolean-like constituent.
|
|
113
|
+
*
|
|
114
|
+
* Example: string | object | null | boolean | number
|
|
115
|
+
*
|
|
116
|
+
* In these cases we avoid rewriting `if (x)` / `if (!x)` because the intent
|
|
117
|
+
* may be genuine boolean gating (or semantic-preserving rewrite isn't clear).
|
|
118
|
+
*/
|
|
119
|
+
function isPartialBooleanUnionByTS(node) {
|
|
120
|
+
const type = getTsType(node);
|
|
121
|
+
if (_.isNil(type))
|
|
122
|
+
return false;
|
|
123
|
+
if (!type.isUnion())
|
|
124
|
+
return false;
|
|
125
|
+
let sawBoolean = false;
|
|
126
|
+
let sawOtherNonNullish = false;
|
|
127
|
+
for (const t of type.types) {
|
|
128
|
+
const flags = t.getFlags();
|
|
129
|
+
if (isNullableFlag(flags))
|
|
130
|
+
continue;
|
|
131
|
+
if (isBooleanLikeFlag(flags)) {
|
|
132
|
+
sawBoolean = true;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
sawOtherNonNullish = true;
|
|
136
|
+
}
|
|
137
|
+
if (sawBoolean && sawOtherNonNullish)
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
89
142
|
/**
|
|
90
143
|
* Returns true iff the expression type is effectively:
|
|
91
144
|
* string | null | undefined
|
|
@@ -201,6 +254,8 @@ const preferExplicitNilCheck = createRule({
|
|
|
201
254
|
return;
|
|
202
255
|
if (isNumberByTS(node))
|
|
203
256
|
return;
|
|
257
|
+
if (isPartialBooleanUnionByTS(node))
|
|
258
|
+
return;
|
|
204
259
|
const text = context.sourceCode.getText(node);
|
|
205
260
|
if (isStringByTS(node)) {
|
|
206
261
|
reportFull(node, `!${LODASH_IDENT}.isEmpty(${text})`);
|
|
@@ -214,6 +269,8 @@ const preferExplicitNilCheck = createRule({
|
|
|
214
269
|
return;
|
|
215
270
|
if (isNumberByTS(arg))
|
|
216
271
|
return;
|
|
272
|
+
if (isPartialBooleanUnionByTS(arg))
|
|
273
|
+
return;
|
|
217
274
|
const text = context.sourceCode.getText(arg);
|
|
218
275
|
if (isStringByTS(arg)) {
|
|
219
276
|
reportFull(node, `${LODASH_IDENT}.isEmpty(${text})`);
|
|
@@ -306,6 +363,8 @@ const preferExplicitNilCheck = createRule({
|
|
|
306
363
|
return;
|
|
307
364
|
if (isBooleanByTS(arg))
|
|
308
365
|
return;
|
|
366
|
+
if (isPartialBooleanUnionByTS(arg))
|
|
367
|
+
return;
|
|
309
368
|
if (isNumberByTS(arg))
|
|
310
369
|
return;
|
|
311
370
|
transformFalsyUnary(node);
|
|
@@ -324,6 +383,8 @@ const preferExplicitNilCheck = createRule({
|
|
|
324
383
|
return;
|
|
325
384
|
if (isBooleanByTS(node))
|
|
326
385
|
return;
|
|
386
|
+
if (isPartialBooleanUnionByTS(node))
|
|
387
|
+
return;
|
|
327
388
|
if (isNumberByTS(node))
|
|
328
389
|
return;
|
|
329
390
|
transformTruthy(node);
|