eslint-plugin-th-rules 3.3.1 → 3.3.3
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.
package/README.md
CHANGED
|
@@ -172,8 +172,9 @@ Do not edit below this line.
|
|
|
172
172
|
| [no-comments](docs/rules/no-comments.md) | Disallow comments except for specified allowed patterns. | ✅ ⚛️ 🟦 🎲 | 🔧 |
|
|
173
173
|
| [no-default-export](docs/rules/no-default-export.md) | Convert unnamed default exports to named default exports based on the file name. | ✅ ⚛️ 🟦 🎲 | 🔧 |
|
|
174
174
|
| [no-destructuring](docs/rules/no-destructuring.md) | Disallow destructuring that does not meet certain conditions. | ✅ ⚛️ 🟦 🎲 | |
|
|
175
|
+
| [no-explicit-nil-check](docs/rules/no-explicit-nil-check.md) | Disallow implicit truthy/falsy checks anywhere. Require explicit _.isNil(value). | ✅ ⚛️ 🟦 🎲 | 🔧 |
|
|
175
176
|
| [no-explicit-nil-compare](docs/rules/no-explicit-nil-compare.md) | Disallow direct comparisons to null or undefined. Use _.isNull(x) / _.isUndefined(x) instead. | ✅ ⚛️ 🟦 🎲 | 🔧 |
|
|
176
|
-
| [prefer-is-empty](docs/rules/prefer-is-empty.md) | Require _.isEmpty instead of length comparisons or
|
|
177
|
+
| [prefer-is-empty](docs/rules/prefer-is-empty.md) | Require _.isEmpty instead of length comparisons or boolean checks on .length. | ✅ ⚛️ 🟦 🎲 | 🔧 |
|
|
177
178
|
| [schemas-in-schemas-file](docs/rules/schemas-in-schemas-file.md) | Require Zod schema declarations to be placed in a .schemas.ts file. | ✅ ⚛️ 🟦 🎲 | |
|
|
178
179
|
| [top-level-functions](docs/rules/top-level-functions.md) | Require all top-level functions to be named regular functions. | ✅ ⚛️ 🟦 🎲 | 🔧 |
|
|
179
180
|
| [types-in-dts](docs/rules/types-in-dts.md) | Require TypeScript type declarations (type/interface/enum) to be placed in .d.ts files. | ✅ ⚛️ 🟦 🎲 | |
|
package/dist/plugin.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export declare const rules: {
|
|
|
20
20
|
'no-explicit-nil-compare': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNull" | "useIsUndefined", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
21
21
|
name: string;
|
|
22
22
|
};
|
|
23
|
-
'
|
|
23
|
+
'prefer-explicit-nil-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNil", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
24
24
|
name: string;
|
|
25
25
|
};
|
|
26
26
|
'prefer-is-empty': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsEmptyUnary" | "useIsEmptyBoolean", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
@@ -66,7 +66,7 @@ declare const plugin: {
|
|
|
66
66
|
'no-explicit-nil-compare': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNull" | "useIsUndefined", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
67
67
|
name: string;
|
|
68
68
|
};
|
|
69
|
-
'
|
|
69
|
+
'prefer-explicit-nil-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNil", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
70
70
|
name: string;
|
|
71
71
|
};
|
|
72
72
|
'prefer-is-empty': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsEmptyUnary" | "useIsEmptyBoolean", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
package/dist/plugin.js
CHANGED
|
@@ -14,7 +14,7 @@ export const rules = {
|
|
|
14
14
|
'no-default-export': noDefaultExport,
|
|
15
15
|
'no-destructuring': noDestructuring,
|
|
16
16
|
'no-explicit-nil-compare': noExplicitNilCompare,
|
|
17
|
-
'
|
|
17
|
+
'prefer-explicit-nil-check': preferExplicitNilCheck,
|
|
18
18
|
'prefer-is-empty': preferIsEmpty,
|
|
19
19
|
'schemas-in-schemas-file': schemasInSchemasFile,
|
|
20
20
|
'top-level-functions': topLevelFunctions,
|
|
@@ -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;;CA0Z1B,CAAC;AAEH,eAAe,sBAAsB,CAAC"}
|
|
@@ -57,23 +57,46 @@ const preferExplicitNilCheck = createRule({
|
|
|
57
57
|
return (flags & ts.TypeFlags.Null) !== 0 || (flags & ts.TypeFlags.Undefined) !== 0;
|
|
58
58
|
}
|
|
59
59
|
function isStringLikeFlag(flags) {
|
|
60
|
-
return (flags & ts.TypeFlags.
|
|
60
|
+
return (flags & ts.TypeFlags.StringLike) !== 0;
|
|
61
61
|
}
|
|
62
62
|
function isNumberLikeFlag(flags) {
|
|
63
|
-
return (flags & ts.TypeFlags.
|
|
63
|
+
return (flags & ts.TypeFlags.NumberLike) !== 0;
|
|
64
|
+
}
|
|
65
|
+
function isBooleanLikeFlag(flags) {
|
|
66
|
+
return (flags & ts.TypeFlags.BooleanLike) !== 0;
|
|
67
|
+
}
|
|
68
|
+
function isAnyOrUnknownFlag(flags) {
|
|
69
|
+
return (flags & ts.TypeFlags.Any) !== 0 || (flags & ts.TypeFlags.Unknown) !== 0;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Skip when the type is `any` or `unknown` (including union constituents),
|
|
73
|
+
* because we can't safely decide whether to prefer isNil/isEmpty without
|
|
74
|
+
* risking semantic changes.
|
|
75
|
+
*/
|
|
76
|
+
function isAnyOrUnknownByTS(node) {
|
|
77
|
+
const type = getTsType(node);
|
|
78
|
+
if (_.isNil(type))
|
|
79
|
+
return false;
|
|
80
|
+
if (!type.isUnion()) {
|
|
81
|
+
return isAnyOrUnknownFlag(type.getFlags());
|
|
82
|
+
}
|
|
83
|
+
for (const t of type.types) {
|
|
84
|
+
if (isAnyOrUnknownFlag(t.getFlags()))
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
64
88
|
}
|
|
65
89
|
/**
|
|
66
90
|
* Returns true iff the expression type is effectively:
|
|
67
91
|
* string | null | undefined
|
|
68
|
-
* (i.e., all non-nullish constituents are string/string-
|
|
92
|
+
* (i.e., all non-nullish constituents are string/string-like).
|
|
69
93
|
*/
|
|
70
94
|
function isStringByTS(node) {
|
|
71
95
|
const type = getTsType(node);
|
|
72
96
|
if (_.isNil(type))
|
|
73
97
|
return false;
|
|
74
98
|
if (!type.isUnion()) {
|
|
75
|
-
|
|
76
|
-
return isStringLikeFlag(flags);
|
|
99
|
+
return isStringLikeFlag(type.getFlags());
|
|
77
100
|
}
|
|
78
101
|
let sawNonNullish = false;
|
|
79
102
|
for (const t of type.types) {
|
|
@@ -98,8 +121,7 @@ const preferExplicitNilCheck = createRule({
|
|
|
98
121
|
if (_.isNil(type))
|
|
99
122
|
return false;
|
|
100
123
|
if (!type.isUnion()) {
|
|
101
|
-
|
|
102
|
-
return isNumberLikeFlag(flags);
|
|
124
|
+
return isNumberLikeFlag(type.getFlags());
|
|
103
125
|
}
|
|
104
126
|
let sawNonNullish = false;
|
|
105
127
|
for (const t of type.types) {
|
|
@@ -112,12 +134,33 @@ const preferExplicitNilCheck = createRule({
|
|
|
112
134
|
}
|
|
113
135
|
return sawNonNullish;
|
|
114
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Returns true iff the expression type is effectively:
|
|
139
|
+
* boolean | null | undefined
|
|
140
|
+
* (i.e., all non-nullish constituents are boolean/boolean-literal).
|
|
141
|
+
*
|
|
142
|
+
* This ensures we do NOT rewrite checks like:
|
|
143
|
+
* - boolean | undefined
|
|
144
|
+
* - boolean | null
|
|
145
|
+
* - boolean | null | undefined
|
|
146
|
+
*/
|
|
115
147
|
function isBooleanByTS(node) {
|
|
116
148
|
const type = getTsType(node);
|
|
117
149
|
if (_.isNil(type))
|
|
118
150
|
return false;
|
|
119
|
-
|
|
120
|
-
|
|
151
|
+
if (!type.isUnion()) {
|
|
152
|
+
return isBooleanLikeFlag(type.getFlags());
|
|
153
|
+
}
|
|
154
|
+
let sawNonNullish = false;
|
|
155
|
+
for (const t of type.types) {
|
|
156
|
+
const flags = t.getFlags();
|
|
157
|
+
if (isNullableFlag(flags))
|
|
158
|
+
continue;
|
|
159
|
+
sawNonNullish = true;
|
|
160
|
+
if (!isBooleanLikeFlag(flags))
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
return sawNonNullish;
|
|
121
164
|
}
|
|
122
165
|
function isAlreadyExplicitCheck(node) {
|
|
123
166
|
return (node.type === AST_NODE_TYPES.CallExpression &&
|
|
@@ -154,6 +197,8 @@ const preferExplicitNilCheck = createRule({
|
|
|
154
197
|
});
|
|
155
198
|
}
|
|
156
199
|
function transformTruthy(node) {
|
|
200
|
+
if (isAnyOrUnknownByTS(node))
|
|
201
|
+
return;
|
|
157
202
|
if (isNumberByTS(node))
|
|
158
203
|
return;
|
|
159
204
|
const text = context.sourceCode.getText(node);
|
|
@@ -165,6 +210,8 @@ const preferExplicitNilCheck = createRule({
|
|
|
165
210
|
}
|
|
166
211
|
function transformFalsyUnary(node) {
|
|
167
212
|
const arg = node.argument;
|
|
213
|
+
if (isAnyOrUnknownByTS(arg))
|
|
214
|
+
return;
|
|
168
215
|
if (isNumberByTS(arg))
|
|
169
216
|
return;
|
|
170
217
|
const text = context.sourceCode.getText(arg);
|
|
@@ -255,6 +302,8 @@ const preferExplicitNilCheck = createRule({
|
|
|
255
302
|
return;
|
|
256
303
|
}
|
|
257
304
|
if (isImplicitOperand(arg)) {
|
|
305
|
+
if (isAnyOrUnknownByTS(arg))
|
|
306
|
+
return;
|
|
258
307
|
if (isBooleanByTS(arg))
|
|
259
308
|
return;
|
|
260
309
|
if (isNumberByTS(arg))
|
|
@@ -271,6 +320,8 @@ const preferExplicitNilCheck = createRule({
|
|
|
271
320
|
}
|
|
272
321
|
default: {
|
|
273
322
|
if (mode === 'test' && isImplicitOperand(node)) {
|
|
323
|
+
if (isAnyOrUnknownByTS(node))
|
|
324
|
+
return;
|
|
274
325
|
if (isBooleanByTS(node))
|
|
275
326
|
return;
|
|
276
327
|
if (isNumberByTS(node))
|