Skip to content

Commit 9535cff

Browse files
authored
feat: support TS syntax in no-loop-func (#19559)
* feat: support TS syntax in `no-loop-func` * fix: update rule meta * chore: remove unwanted formatting * test: add more cases * chore: fix lint * fix: add more test cases * chore: fix formatting
1 parent bd05397 commit 9535cff

File tree

3 files changed

+207
-0
lines changed

3 files changed

+207
-0
lines changed

docs/src/rules/no-loop-func.md

+18
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,24 @@ for (var i = 0; i < 5; i++) {
141141

142142
:::
143143

144+
This rule additionally supports TypeScript type syntax.
145+
146+
Examples of **correct** TypeScript code for this rule:
147+
148+
::: correct
149+
150+
```ts
151+
/*eslint no-loop-func: "error"*/
152+
153+
type MyType = 1;
154+
let someArray: MyType[] = [];
155+
for (let i = 0; i < 10; i += 1) {
156+
someArray = someArray.filter((item: MyType) => !!item);
157+
}
158+
```
159+
160+
:::
161+
144162
## Known Limitations
145163

146164
The rule cannot identify whether the function instance is just immediately invoked and then discarded, or possibly stored for later use.

lib/rules/no-loop-func.js

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ function isIIFE(node) {
3232
module.exports = {
3333
meta: {
3434
type: "suggestion",
35+
dialects: ["typescript", "javascript"],
36+
language: "javascript",
3537

3638
docs: {
3739
description:

tests/lib/rules/no-loop-func.js

+187
Original file line numberDiff line numberDiff line change
@@ -765,3 +765,190 @@ ruleTester.run("no-loop-func", rule, {
765765
},
766766
],
767767
});
768+
769+
const ruleTesterTypeScript = new RuleTester({
770+
languageOptions: {
771+
parser: require("@typescript-eslint/parser"),
772+
},
773+
});
774+
775+
ruleTesterTypeScript.run("no-loop-func", rule, {
776+
valid: [
777+
`
778+
for (let i = 0; i < 10; i++) {
779+
function foo() {
780+
console.log('A');
781+
}
782+
}
783+
`,
784+
`
785+
let someArray: MyType[] = [];
786+
for (let i = 0; i < 10; i += 1) {
787+
someArray = someArray.filter((item: MyType) => !!item);
788+
}
789+
`,
790+
{
791+
code: `
792+
let someArray: MyType[] = [];
793+
for (let i = 0; i < 10; i += 1) {
794+
someArray = someArray.filter((item: MyType) => !!item);
795+
}
796+
`,
797+
languageOptions: {
798+
globals: {
799+
MyType: "readonly",
800+
},
801+
},
802+
},
803+
{
804+
code: `
805+
let someArray: MyType[] = [];
806+
for (let i = 0; i < 10; i += 1) {
807+
someArray = someArray.filter((item: MyType) => !!item);
808+
}
809+
`,
810+
languageOptions: {
811+
globals: {
812+
MyType: "writable",
813+
},
814+
},
815+
},
816+
`
817+
type MyType = 1;
818+
let someArray: MyType[] = [];
819+
for (let i = 0; i < 10; i += 1) {
820+
someArray = someArray.filter((item: MyType) => !!item);
821+
}
822+
`,
823+
// Test case for a global type that hasn't been configured
824+
`
825+
// UnconfiguredGlobalType is not defined anywhere or configured in globals
826+
for (var i = 0; i < 10; i++) {
827+
const process = (item: UnconfiguredGlobalType) => {
828+
// This is valid because the type reference is considered safe
829+
// even though UnconfiguredGlobalType is not configured
830+
return item.id;
831+
};
832+
}
833+
`,
834+
835+
// Test both configured and unconfigured global types
836+
{
837+
code: `
838+
for (var i = 0; i < 10; i++) {
839+
// ConfiguredType is in globals, UnconfiguredType is not
840+
// Both should be considered safe as they are type references
841+
const process = (configItem: ConfiguredType, unconfigItem: UnconfiguredType) => {
842+
return {
843+
config: configItem.value,
844+
unconfig: unconfigItem.value
845+
};
846+
};
847+
}
848+
`,
849+
languageOptions: {
850+
globals: {
851+
ConfiguredType: "readonly",
852+
},
853+
},
854+
},
855+
],
856+
invalid: [
857+
{
858+
code: `
859+
for (var i = 0; i < 10; i++) {
860+
function foo() {
861+
console.log(i);
862+
}
863+
}
864+
`,
865+
errors: [
866+
{
867+
messageId: "unsafeRefs",
868+
data: { varNames: "'i'" },
869+
type: "FunctionDeclaration",
870+
},
871+
],
872+
},
873+
{
874+
code: `
875+
for (var i = 0; i < 10; i++) {
876+
const handler = (event: Event) => {
877+
console.log(i);
878+
};
879+
}
880+
`,
881+
errors: [
882+
{
883+
messageId: "unsafeRefs",
884+
data: { varNames: "'i'" },
885+
type: "ArrowFunctionExpression",
886+
},
887+
],
888+
},
889+
{
890+
code: `
891+
interface Item {
892+
id: number;
893+
name: string;
894+
}
895+
896+
const items: Item[] = [];
897+
for (var i = 0; i < 10; i++) {
898+
items.push({
899+
id: i,
900+
name: "Item " + i
901+
});
902+
903+
const process = function(callback: (item: Item) => void): void {
904+
callback({ id: i, name: "Item " + i });
905+
};
906+
}
907+
`,
908+
errors: [
909+
{
910+
messageId: "unsafeRefs",
911+
data: { varNames: "'i', 'i'" },
912+
type: "FunctionExpression",
913+
},
914+
],
915+
},
916+
{
917+
code: `
918+
type Processor<T> = (item: T) => void;
919+
920+
for (var i = 0; i < 10; i++) {
921+
const processor: Processor<number> = (item) => {
922+
return item + i;
923+
};
924+
}
925+
`,
926+
errors: [
927+
{
928+
messageId: "unsafeRefs",
929+
data: { varNames: "'i'" },
930+
type: "ArrowFunctionExpression",
931+
},
932+
],
933+
},
934+
// Function uses unconfigured global type but still references loop variable
935+
{
936+
code: `
937+
for (var i = 0; i < 10; i++) {
938+
// UnconfiguredGlobalType is not defined anywhere
939+
// But the function still references i which makes it unsafe
940+
const process = (item: UnconfiguredGlobalType) => {
941+
console.log(i, item.value);
942+
};
943+
}
944+
`,
945+
errors: [
946+
{
947+
messageId: "unsafeRefs",
948+
data: { varNames: "'i'" },
949+
type: "ArrowFunctionExpression",
950+
},
951+
],
952+
},
953+
],
954+
});

0 commit comments

Comments
 (0)