@flint.fyi/ts 0.14.2 → 0.14.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.
Files changed (209) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/lib/index.d.ts +1 -0
  3. package/lib/index.js +1 -0
  4. package/lib/nodes.d.ts +1 -0
  5. package/lib/plugin.d.ts +121 -1
  6. package/lib/plugin.js +60 -0
  7. package/lib/rules/asyncPromiseExecutors.d.ts +6 -0
  8. package/lib/rules/asyncPromiseExecutors.js +51 -0
  9. package/lib/rules/asyncPromiseExecutors.test.d.ts +1 -0
  10. package/lib/rules/asyncPromiseExecutors.test.js +71 -0
  11. package/lib/rules/caseDeclarations.d.ts +6 -0
  12. package/lib/rules/caseDeclarations.js +53 -0
  13. package/lib/rules/caseDeclarations.test.d.ts +1 -0
  14. package/lib/rules/caseDeclarations.test.js +227 -0
  15. package/lib/rules/chainedAssignments.d.ts +6 -0
  16. package/lib/rules/chainedAssignments.js +48 -0
  17. package/lib/rules/chainedAssignments.test.d.ts +1 -0
  18. package/lib/rules/chainedAssignments.test.js +238 -0
  19. package/lib/rules/classAssignments.d.ts +6 -0
  20. package/lib/rules/classAssignments.js +42 -0
  21. package/lib/rules/classAssignments.test.d.ts +1 -0
  22. package/lib/rules/classAssignments.test.js +156 -0
  23. package/lib/rules/constantAssignments.d.ts +6 -0
  24. package/lib/rules/constantAssignments.js +61 -0
  25. package/lib/rules/constantAssignments.test.d.ts +1 -0
  26. package/lib/rules/constantAssignments.test.js +221 -0
  27. package/lib/rules/constructorReturns.d.ts +6 -0
  28. package/lib/rules/constructorReturns.js +52 -0
  29. package/lib/rules/constructorReturns.test.d.ts +1 -0
  30. package/lib/rules/constructorReturns.test.js +96 -0
  31. package/lib/rules/defaultCaseLast.d.ts +6 -0
  32. package/lib/rules/defaultCaseLast.js +43 -0
  33. package/lib/rules/defaultCaseLast.test.d.ts +1 -0
  34. package/lib/rules/defaultCaseLast.test.js +195 -0
  35. package/lib/rules/duplicateArguments.d.ts +6 -0
  36. package/lib/rules/duplicateArguments.js +51 -0
  37. package/lib/rules/duplicateArguments.test.d.ts +1 -0
  38. package/lib/rules/duplicateArguments.test.js +139 -0
  39. package/lib/rules/emptyDestructures.d.ts +6 -0
  40. package/lib/rules/emptyDestructures.js +40 -0
  41. package/lib/rules/emptyDestructures.test.d.ts +1 -0
  42. package/lib/rules/emptyDestructures.test.js +186 -0
  43. package/lib/rules/emptyStaticBlocks.d.ts +6 -0
  44. package/lib/rules/emptyStaticBlocks.js +54 -0
  45. package/lib/rules/emptyStaticBlocks.test.d.ts +1 -0
  46. package/lib/rules/emptyStaticBlocks.test.js +167 -0
  47. package/lib/rules/exceptionAssignments.d.ts +6 -0
  48. package/lib/rules/exceptionAssignments.js +61 -0
  49. package/lib/rules/exceptionAssignments.test.d.ts +1 -0
  50. package/lib/rules/exceptionAssignments.test.js +264 -0
  51. package/lib/rules/functionAssignments.d.ts +6 -0
  52. package/lib/rules/functionAssignments.js +42 -0
  53. package/lib/rules/functionAssignments.test.d.ts +1 -0
  54. package/lib/rules/functionAssignments.test.js +209 -0
  55. package/lib/rules/generatorFunctionYields.d.ts +6 -0
  56. package/lib/rules/generatorFunctionYields.js +57 -0
  57. package/lib/rules/generatorFunctionYields.test.d.ts +1 -0
  58. package/lib/rules/generatorFunctionYields.test.js +151 -0
  59. package/lib/rules/globalAssignments.d.ts +6 -0
  60. package/lib/rules/globalAssignments.js +59 -0
  61. package/lib/rules/globalAssignments.test.d.ts +1 -0
  62. package/lib/rules/globalAssignments.test.js +215 -0
  63. package/lib/rules/globalObjectCalls.d.ts +6 -0
  64. package/lib/rules/globalObjectCalls.js +43 -0
  65. package/lib/rules/globalObjectCalls.test.d.ts +1 -0
  66. package/lib/rules/globalObjectCalls.test.js +93 -0
  67. package/lib/rules/negativeZeroComparisons.d.ts +6 -0
  68. package/lib/rules/negativeZeroComparisons.js +83 -0
  69. package/lib/rules/negativeZeroComparisons.test.d.ts +1 -0
  70. package/lib/rules/negativeZeroComparisons.test.js +209 -0
  71. package/lib/rules/newExpressions.d.ts +6 -0
  72. package/lib/rules/newExpressions.js +54 -0
  73. package/lib/rules/newExpressions.test.d.ts +1 -0
  74. package/lib/rules/newExpressions.test.js +79 -0
  75. package/lib/rules/newNativeNonConstructors.d.ts +6 -0
  76. package/lib/rules/newNativeNonConstructors.js +49 -0
  77. package/lib/rules/newNativeNonConstructors.test.d.ts +1 -0
  78. package/lib/rules/newNativeNonConstructors.test.js +94 -0
  79. package/lib/rules/objectProto.d.ts +6 -0
  80. package/lib/rules/objectProto.js +54 -0
  81. package/lib/rules/objectProto.test.d.ts +1 -0
  82. package/lib/rules/objectProto.test.js +96 -0
  83. package/lib/rules/octalNumbers.d.ts +6 -0
  84. package/lib/rules/octalNumbers.js +68 -0
  85. package/lib/rules/octalNumbers.test.d.ts +1 -0
  86. package/lib/rules/octalNumbers.test.js +245 -0
  87. package/lib/rules/returnAssignments.d.ts +6 -0
  88. package/lib/rules/returnAssignments.js +50 -0
  89. package/lib/rules/returnAssignments.test.d.ts +1 -0
  90. package/lib/rules/returnAssignments.test.js +185 -0
  91. package/lib/rules/sparseArrays.d.ts +6 -0
  92. package/lib/rules/sparseArrays.js +54 -0
  93. package/lib/rules/sparseArrays.test.d.ts +1 -0
  94. package/lib/rules/sparseArrays.test.js +110 -0
  95. package/lib/rules/symbolDescriptions.d.ts +6 -0
  96. package/lib/rules/symbolDescriptions.js +41 -0
  97. package/lib/rules/symbolDescriptions.test.d.ts +1 -0
  98. package/lib/rules/symbolDescriptions.test.js +89 -0
  99. package/lib/rules/unassignedVariables.d.ts +6 -0
  100. package/lib/rules/unassignedVariables.js +55 -0
  101. package/lib/rules/unassignedVariables.test.d.ts +1 -0
  102. package/lib/rules/unassignedVariables.test.js +72 -0
  103. package/lib/rules/undefinedVariables.d.ts +6 -0
  104. package/lib/rules/undefinedVariables.js +76 -0
  105. package/lib/rules/undefinedVariables.test.d.ts +1 -0
  106. package/lib/rules/undefinedVariables.test.js +80 -0
  107. package/lib/rules/unicodeBOMs.d.ts +6 -0
  108. package/lib/rules/unicodeBOMs.js +41 -0
  109. package/lib/rules/unicodeBOMs.test.d.ts +1 -0
  110. package/lib/rules/unicodeBOMs.test.js +65 -0
  111. package/lib/rules/unnecessaryCatches.d.ts +6 -0
  112. package/lib/rules/unnecessaryCatches.js +68 -0
  113. package/lib/rules/unnecessaryCatches.test.d.ts +1 -0
  114. package/lib/rules/unnecessaryCatches.test.js +210 -0
  115. package/lib/rules/unsafeNegations.d.ts +6 -0
  116. package/lib/rules/unsafeNegations.js +50 -0
  117. package/lib/rules/unsafeNegations.test.d.ts +1 -0
  118. package/lib/rules/unsafeNegations.test.js +63 -0
  119. package/lib/rules/variableDeletions.d.ts +6 -0
  120. package/lib/rules/variableDeletions.js +39 -0
  121. package/lib/rules/variableDeletions.test.d.ts +1 -0
  122. package/lib/rules/variableDeletions.test.js +75 -0
  123. package/lib/rules/voidOperator.d.ts +6 -0
  124. package/lib/rules/voidOperator.js +36 -0
  125. package/lib/rules/voidOperator.test.d.ts +1 -0
  126. package/lib/rules/voidOperator.test.js +98 -0
  127. package/lib/utils/declarationsIncludeGlobal.d.ts +2 -0
  128. package/lib/utils/declarationsIncludeGlobal.js +7 -0
  129. package/lib/utils/getModifyingReferences.d.ts +8 -0
  130. package/lib/utils/getModifyingReferences.js +42 -0
  131. package/lib/utils/isGlobalDeclaration.d.ts +2 -0
  132. package/lib/utils/isGlobalDeclaration.js +5 -0
  133. package/lib/utils/isGlobalVariable.d.ts +8 -0
  134. package/lib/utils/isGlobalVariable.js +21 -0
  135. package/lib/utils/unwrapParentParenthesizedExpressions.d.ts +2 -0
  136. package/lib/utils/unwrapParentParenthesizedExpressions.js +6 -0
  137. package/lib/utils/unwrapParenthesizedExpression.d.ts +2 -0
  138. package/lib/utils/unwrapParenthesizedExpression.js +6 -0
  139. package/package.json +1 -1
  140. package/src/index.ts +1 -0
  141. package/src/nodes.ts +1 -0
  142. package/src/plugin.ts +60 -0
  143. package/src/rules/asyncPromiseExecutors.test.ts +72 -0
  144. package/src/rules/asyncPromiseExecutors.ts +61 -0
  145. package/src/rules/caseDeclarations.test.ts +228 -0
  146. package/src/rules/caseDeclarations.ts +70 -0
  147. package/src/rules/chainedAssignments.test.ts +239 -0
  148. package/src/rules/chainedAssignments.ts +55 -0
  149. package/src/rules/classAssignments.test.ts +157 -0
  150. package/src/rules/classAssignments.ts +50 -0
  151. package/src/rules/constantAssignments.test.ts +222 -0
  152. package/src/rules/constantAssignments.ts +78 -0
  153. package/src/rules/constructorReturns.test.ts +97 -0
  154. package/src/rules/constructorReturns.ts +59 -0
  155. package/src/rules/defaultCaseLast.test.ts +196 -0
  156. package/src/rules/defaultCaseLast.ts +54 -0
  157. package/src/rules/duplicateArguments.test.ts +140 -0
  158. package/src/rules/duplicateArguments.ts +58 -0
  159. package/src/rules/emptyDestructures.test.ts +187 -0
  160. package/src/rules/emptyDestructures.ts +48 -0
  161. package/src/rules/emptyStaticBlocks.test.ts +168 -0
  162. package/src/rules/emptyStaticBlocks.ts +60 -0
  163. package/src/rules/exceptionAssignments.test.ts +265 -0
  164. package/src/rules/exceptionAssignments.ts +77 -0
  165. package/src/rules/functionAssignments.test.ts +210 -0
  166. package/src/rules/functionAssignments.ts +51 -0
  167. package/src/rules/generatorFunctionYields.test.ts +152 -0
  168. package/src/rules/generatorFunctionYields.ts +71 -0
  169. package/src/rules/globalAssignments.test.ts +216 -0
  170. package/src/rules/globalAssignments.ts +67 -0
  171. package/src/rules/globalObjectCalls.test.ts +94 -0
  172. package/src/rules/globalObjectCalls.ts +52 -0
  173. package/src/rules/negativeZeroComparisons.test.ts +210 -0
  174. package/src/rules/negativeZeroComparisons.ts +106 -0
  175. package/src/rules/newExpressions.test.ts +80 -0
  176. package/src/rules/newExpressions.ts +67 -0
  177. package/src/rules/newNativeNonConstructors.test.ts +95 -0
  178. package/src/rules/newNativeNonConstructors.ts +59 -0
  179. package/src/rules/objectProto.test.ts +97 -0
  180. package/src/rules/objectProto.ts +62 -0
  181. package/src/rules/octalNumbers.test.ts +246 -0
  182. package/src/rules/octalNumbers.ts +75 -0
  183. package/src/rules/returnAssignments.test.ts +186 -0
  184. package/src/rules/returnAssignments.ts +57 -0
  185. package/src/rules/sparseArrays.test.ts +111 -0
  186. package/src/rules/sparseArrays.ts +61 -0
  187. package/src/rules/symbolDescriptions.test.ts +90 -0
  188. package/src/rules/symbolDescriptions.ts +47 -0
  189. package/src/rules/unassignedVariables.test.ts +73 -0
  190. package/src/rules/unassignedVariables.ts +69 -0
  191. package/src/rules/undefinedVariables.test.ts +81 -0
  192. package/src/rules/undefinedVariables.ts +97 -0
  193. package/src/rules/unicodeBOMs.test.ts +66 -0
  194. package/src/rules/unicodeBOMs.ts +44 -0
  195. package/src/rules/unnecessaryCatches.test.ts +211 -0
  196. package/src/rules/unnecessaryCatches.ts +84 -0
  197. package/src/rules/unsafeNegations.test.ts +64 -0
  198. package/src/rules/unsafeNegations.ts +59 -0
  199. package/src/rules/variableDeletions.test.ts +76 -0
  200. package/src/rules/variableDeletions.ts +43 -0
  201. package/src/rules/voidOperator.test.ts +99 -0
  202. package/src/rules/voidOperator.ts +38 -0
  203. package/src/utils/declarationsIncludeGlobal.ts +11 -0
  204. package/src/utils/getModifyingReferences.ts +58 -0
  205. package/src/utils/isGlobalDeclaration.ts +12 -0
  206. package/src/utils/isGlobalVariable.ts +30 -0
  207. package/src/utils/unwrapParentParenthesizedExpressions.ts +7 -0
  208. package/src/utils/unwrapParenthesizedExpression.ts +9 -0
  209. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,228 @@
1
+ import rule from "./caseDeclarations.js";
2
+ import { ruleTester } from "./ruleTester.js";
3
+
4
+ ruleTester.describe(rule, {
5
+ invalid: [
6
+ {
7
+ code: `
8
+ switch (value) {
9
+ case 1:
10
+ let x = 1;
11
+ break;
12
+ }
13
+ `,
14
+ snapshot: `
15
+ switch (value) {
16
+ case 1:
17
+ let x = 1;
18
+ ~~~
19
+ Variables declared in case clauses without braces leak into the surrounding scope.
20
+ break;
21
+ }
22
+ `,
23
+ },
24
+ {
25
+ code: `
26
+ switch (value) {
27
+ case 1:
28
+ const x = 1;
29
+ break;
30
+ }
31
+ `,
32
+ snapshot: `
33
+ switch (value) {
34
+ case 1:
35
+ const x = 1;
36
+ ~~~~~
37
+ Variables declared in case clauses without braces leak into the surrounding scope.
38
+ break;
39
+ }
40
+ `,
41
+ },
42
+ {
43
+ code: `
44
+ switch (value) {
45
+ case 1:
46
+ function foo() {}
47
+ break;
48
+ }
49
+ `,
50
+ snapshot: `
51
+ switch (value) {
52
+ case 1:
53
+ function foo() {}
54
+ ~~~~~~~~
55
+ Variables declared in case clauses without braces leak into the surrounding scope.
56
+ break;
57
+ }
58
+ `,
59
+ },
60
+ {
61
+ code: `
62
+ switch (value) {
63
+ case 1:
64
+ class Foo {}
65
+ break;
66
+ }
67
+ `,
68
+ snapshot: `
69
+ switch (value) {
70
+ case 1:
71
+ class Foo {}
72
+ ~~~~~
73
+ Variables declared in case clauses without braces leak into the surrounding scope.
74
+ break;
75
+ }
76
+ `,
77
+ },
78
+ {
79
+ code: `
80
+ switch (value) {
81
+ default:
82
+ let x = 1;
83
+ break;
84
+ }
85
+ `,
86
+ snapshot: `
87
+ switch (value) {
88
+ default:
89
+ let x = 1;
90
+ ~~~
91
+ Variables declared in case clauses without braces leak into the surrounding scope.
92
+ break;
93
+ }
94
+ `,
95
+ },
96
+ {
97
+ code: `
98
+ switch (value) {
99
+ default:
100
+ const x = 1;
101
+ break;
102
+ }
103
+ `,
104
+ snapshot: `
105
+ switch (value) {
106
+ default:
107
+ const x = 1;
108
+ ~~~~~
109
+ Variables declared in case clauses without braces leak into the surrounding scope.
110
+ break;
111
+ }
112
+ `,
113
+ },
114
+ {
115
+ code: `
116
+ switch (value) {
117
+ case 1:
118
+ let x = 1;
119
+ case 2:
120
+ let y = 2;
121
+ break;
122
+ }
123
+ `,
124
+ snapshot: `
125
+ switch (value) {
126
+ case 1:
127
+ let x = 1;
128
+ ~~~
129
+ Variables declared in case clauses without braces leak into the surrounding scope.
130
+ case 2:
131
+ let y = 2;
132
+ ~~~
133
+ Variables declared in case clauses without braces leak into the surrounding scope.
134
+ break;
135
+ }
136
+ `,
137
+ },
138
+ {
139
+ code: `
140
+ switch (value) {
141
+ case 1:
142
+ const x = 1;
143
+ console.log(x);
144
+ break;
145
+ }
146
+ `,
147
+ snapshot: `
148
+ switch (value) {
149
+ case 1:
150
+ const x = 1;
151
+ ~~~~~
152
+ Variables declared in case clauses without braces leak into the surrounding scope.
153
+ console.log(x);
154
+ break;
155
+ }
156
+ `,
157
+ },
158
+ ],
159
+ valid: [
160
+ `switch (value) { case 1: { let x = 1; break; } }`,
161
+ `switch (value) { case 1: { const x = 1; break; } }`,
162
+ `switch (value) { case 1: { function foo() {} break; } }`,
163
+ `switch (value) { case 1: { class Foo {} break; } }`,
164
+ `switch (value) { default: { let x = 1; break; } }`,
165
+ `switch (value) { default: { const x = 1; break; } }`,
166
+ `switch (value) { case 1: var x = 1; break; }`,
167
+ `switch (value) { case 1: break; }`,
168
+ `switch (value) { case 1: console.log("test"); break; }`,
169
+ `
170
+ switch (value) {
171
+ case 1: {
172
+ let x = 1;
173
+ break;
174
+ }
175
+ }
176
+ `,
177
+ `
178
+ switch (value) {
179
+ case 1: {
180
+ const x = 1;
181
+ break;
182
+ }
183
+ }
184
+ `,
185
+ `
186
+ switch (value) {
187
+ case 1: {
188
+ function foo() {}
189
+ break;
190
+ }
191
+ }
192
+ `,
193
+ `
194
+ switch (value) {
195
+ case 1: {
196
+ class Foo {}
197
+ break;
198
+ }
199
+ }
200
+ `,
201
+ `
202
+ switch (value) {
203
+ case 1:
204
+ var x = 1;
205
+ break;
206
+ }
207
+ `,
208
+ `
209
+ switch (value) {
210
+ case 1: {
211
+ let x = 1;
212
+ }
213
+ case 2: {
214
+ let y = 2;
215
+ break;
216
+ }
217
+ }
218
+ `,
219
+ `
220
+ switch (value) {
221
+ default: {
222
+ let x = 1;
223
+ break;
224
+ }
225
+ }
226
+ `,
227
+ ],
228
+ });
@@ -0,0 +1,70 @@
1
+ import * as tsutils from "ts-api-utils";
2
+ import * as ts from "typescript";
3
+
4
+ import { getTSNodeRange } from "../getTSNodeRange.js";
5
+ import { typescriptLanguage } from "../language.js";
6
+
7
+ export default typescriptLanguage.createRule({
8
+ about: {
9
+ description:
10
+ "Reports lexical declarations in case clauses without wrapping them in blocks.",
11
+ id: "caseDeclarations",
12
+ preset: "untyped",
13
+ },
14
+ messages: {
15
+ unexpectedLexicalDeclaration: {
16
+ primary:
17
+ "Variables declared in case clauses without braces leak into the surrounding scope.",
18
+ secondary: [
19
+ "Lexical declarations (let, const, function, class) are scoped to the entire switch statement, not just the case clause where they are declared.",
20
+ "This can lead to unexpected behavior when the same variable name is used in multiple case clauses, as they will conflict in the same scope.",
21
+ ],
22
+ suggestions: [
23
+ "Wrap the case clause contents in curly braces {} to create a block scope.",
24
+ ],
25
+ },
26
+ },
27
+ setup(context) {
28
+ function getLexicalDeclaration(
29
+ statements: ts.NodeArray<ts.Statement>,
30
+ ): ts.Node | undefined {
31
+ for (const statement of statements) {
32
+ if (
33
+ ts.isVariableStatement(statement) &&
34
+ tsutils.isNodeFlagSet(
35
+ statement.declarationList,
36
+ ts.NodeFlags.Let | ts.NodeFlags.Const,
37
+ )
38
+ ) {
39
+ return statement.declarationList.getChildAt(0, context.sourceFile);
40
+ }
41
+
42
+ if (
43
+ ts.isClassDeclaration(statement) ||
44
+ ts.isFunctionDeclaration(statement)
45
+ ) {
46
+ return statement.getChildAt(0, context.sourceFile);
47
+ }
48
+ }
49
+
50
+ return undefined;
51
+ }
52
+
53
+ function checkClause(node: ts.CaseClause | ts.DefaultClause): void {
54
+ const declarationNode = getLexicalDeclaration(node.statements);
55
+ if (declarationNode) {
56
+ context.report({
57
+ message: "unexpectedLexicalDeclaration",
58
+ range: getTSNodeRange(declarationNode, context.sourceFile),
59
+ });
60
+ }
61
+ }
62
+
63
+ return {
64
+ visitors: {
65
+ CaseClause: checkClause,
66
+ DefaultClause: checkClause,
67
+ },
68
+ };
69
+ },
70
+ });
@@ -0,0 +1,239 @@
1
+ import rule from "./chainedAssignments.js";
2
+ import { ruleTester } from "./ruleTester.js";
3
+
4
+ ruleTester.describe(rule, {
5
+ invalid: [
6
+ {
7
+ code: `
8
+ let first;
9
+ let second;
10
+ first = second = 1;
11
+ `,
12
+ snapshot: `
13
+ let first;
14
+ let second;
15
+ first = second = 1;
16
+ ~
17
+ Prefer separate assignment statements for readability instead of chaining assignments.
18
+ `,
19
+ },
20
+ {
21
+ code: `
22
+ let first;
23
+ let second;
24
+ let third;
25
+ first = second = third = 0;
26
+ `,
27
+ snapshot: `
28
+ let first;
29
+ let second;
30
+ let third;
31
+ first = second = third = 0;
32
+ ~
33
+ Prefer separate assignment statements for readability instead of chaining assignments.
34
+ `,
35
+ },
36
+ {
37
+ code: `
38
+ let value;
39
+ let another;
40
+ value = another = getValue();
41
+ `,
42
+ snapshot: `
43
+ let value;
44
+ let another;
45
+ value = another = getValue();
46
+ ~
47
+ Prefer separate assignment statements for readability instead of chaining assignments.
48
+ `,
49
+ },
50
+ {
51
+ code: `
52
+ let first;
53
+ let second;
54
+ (first = second = 1);
55
+ `,
56
+ snapshot: `
57
+ let first;
58
+ let second;
59
+ (first = second = 1);
60
+ ~
61
+ Prefer separate assignment statements for readability instead of chaining assignments.
62
+ `,
63
+ },
64
+ {
65
+ code: `
66
+ const calculate = (value: number) => {
67
+ let first;
68
+ let second;
69
+ first = second = value;
70
+ return first + second;
71
+ };
72
+ `,
73
+ snapshot: `
74
+ const calculate = (value: number) => {
75
+ let first;
76
+ let second;
77
+ first = second = value;
78
+ ~
79
+ Prefer separate assignment statements for readability instead of chaining assignments.
80
+ return first + second;
81
+ };
82
+ `,
83
+ },
84
+ {
85
+ code: `
86
+ let first;
87
+ let second;
88
+ if (true) {
89
+ first = second = 10;
90
+ }
91
+ `,
92
+ snapshot: `
93
+ let first;
94
+ let second;
95
+ if (true) {
96
+ first = second = 10;
97
+ ~
98
+ Prefer separate assignment statements for readability instead of chaining assignments.
99
+ }
100
+ `,
101
+ },
102
+ {
103
+ code: `
104
+ class MyClass {
105
+ method() {
106
+ let first;
107
+ let second;
108
+ first = second = 0;
109
+ }
110
+ }
111
+ `,
112
+ snapshot: `
113
+ class MyClass {
114
+ method() {
115
+ let first;
116
+ let second;
117
+ first = second = 0;
118
+ ~
119
+ Prefer separate assignment statements for readability instead of chaining assignments.
120
+ }
121
+ }
122
+ `,
123
+ },
124
+ {
125
+ code: `
126
+ let first;
127
+ let second;
128
+ let third;
129
+ first = (second = third = 5);
130
+ `,
131
+ snapshot: `
132
+ let first;
133
+ let second;
134
+ let third;
135
+ first = (second = third = 5);
136
+ ~
137
+ Prefer separate assignment statements for readability instead of chaining assignments.
138
+ `,
139
+ },
140
+ {
141
+ code: `
142
+ let first;
143
+ let second;
144
+ first ||= second ||= 1;
145
+ `,
146
+ snapshot: `
147
+ let first;
148
+ let second;
149
+ first ||= second ||= 1;
150
+ ~~~
151
+ Prefer separate assignment statements for readability instead of chaining assignments.
152
+ `,
153
+ },
154
+ {
155
+ code: `
156
+ let first;
157
+ let second;
158
+ first ??= second ??= 0;
159
+ `,
160
+ snapshot: `
161
+ let first;
162
+ let second;
163
+ first ??= second ??= 0;
164
+ ~~~
165
+ Prefer separate assignment statements for readability instead of chaining assignments.
166
+ `,
167
+ },
168
+ {
169
+ code: `
170
+ let first;
171
+ let second;
172
+ first += second += 5;
173
+ `,
174
+ snapshot: `
175
+ let first;
176
+ let second;
177
+ first += second += 5;
178
+ ~~
179
+ Prefer separate assignment statements for readability instead of chaining assignments.
180
+ `,
181
+ },
182
+ {
183
+ code: `
184
+ let first;
185
+ let second;
186
+ first |= second |= 3;
187
+ `,
188
+ snapshot: `
189
+ let first;
190
+ let second;
191
+ first |= second |= 3;
192
+ ~~
193
+ Prefer separate assignment statements for readability instead of chaining assignments.
194
+ `,
195
+ },
196
+ ],
197
+ valid: [
198
+ `let first = 1;`,
199
+ `let first = 1, second = 2;`,
200
+ `let first = 1; let second = 2;`,
201
+ `let first; let second; first = 1; second = 2;`,
202
+ `let first; let second; first = 1; second = first;`,
203
+ `const value = getValue();`,
204
+ `let first; first = 1;`,
205
+ `
206
+ let first;
207
+ let second;
208
+ first = 1;
209
+ second = 2;
210
+ `,
211
+ `
212
+ let first;
213
+ let second;
214
+ let third;
215
+ first = 1;
216
+ second = 2;
217
+ third = 3;
218
+ `,
219
+ `
220
+ const calculate = (value: number) => {
221
+ let first;
222
+ let second;
223
+ first = value;
224
+ second = value;
225
+ return first + second;
226
+ };
227
+ `,
228
+ `
229
+ class MyClass {
230
+ method() {
231
+ let first;
232
+ let second;
233
+ first = 0;
234
+ second = 0;
235
+ }
236
+ }
237
+ `,
238
+ ],
239
+ });
@@ -0,0 +1,55 @@
1
+ import * as tsutils from "ts-api-utils";
2
+ import * as ts from "typescript";
3
+
4
+ import { getTSNodeRange } from "../getTSNodeRange.js";
5
+ import { typescriptLanguage } from "../language.js";
6
+ import { unwrapParenthesizedExpression } from "../utils/unwrapParenthesizedExpression.js";
7
+ import { unwrapParenthesizedExpressionsParent } from "../utils/unwrapParentParenthesizedExpressions.js";
8
+
9
+ export default typescriptLanguage.createRule({
10
+ about: {
11
+ description:
12
+ "Reports using chained assignment expressions (e.g., a = b = c).",
13
+ id: "chainedAssignments",
14
+ preset: "stylistic",
15
+ },
16
+ messages: {
17
+ noChainedAssignment: {
18
+ primary:
19
+ "Prefer separate assignment statements for readability instead of chaining assignments.",
20
+ secondary: [
21
+ "Chained assignments can be hard to read and can lead to unexpected behavior with variable scoping and type inference.",
22
+ "Each assignment creates a reference to the same value, which may cause confusion when dealing with mutable values.",
23
+ ],
24
+ suggestions: [
25
+ "Break the chained assignment into separate assignment statements, one for each variable.",
26
+ ],
27
+ },
28
+ },
29
+ setup(context) {
30
+ return {
31
+ visitors: {
32
+ BinaryExpression: (node) => {
33
+ if (!tsutils.isAssignmentKind(node.operatorToken.kind)) {
34
+ return;
35
+ }
36
+
37
+ const rightSide = unwrapParenthesizedExpression(node.right);
38
+ if (!ts.isBinaryExpression(rightSide)) {
39
+ return;
40
+ }
41
+
42
+ const parent = unwrapParenthesizedExpressionsParent(node);
43
+ if (ts.isBinaryExpression(parent)) {
44
+ return;
45
+ }
46
+
47
+ context.report({
48
+ message: "noChainedAssignment",
49
+ range: getTSNodeRange(node.operatorToken, context.sourceFile),
50
+ });
51
+ },
52
+ },
53
+ };
54
+ },
55
+ });