Repository: typescript-eslint/tslint-to-eslint-config (ts)

Issue:
Missing converter: no-for-in
### 💥 Missing Converter

-   `tslint-to-eslint-config` version: 1.2.0
-   `typescript-eslint` version: 2.3.2

#### TSLint Rule Name

`no-for-in`


Pull request:
Adds no-for-in converter along with tests
<!--
👋 Hi, thanks for sending a PR to tslint-to-eslint-config! 💖
Please fill out all fields below to ensure your PR is reviewed quickly.
-->

## PR Checklist

-   [x] Addresses an existing issue: fixes #170
-   [x] That issue was marked as [`status: accepting prs`](https://github.com/typescript-eslint/tslint-to-eslint-config/labels/status%3A%20accepting%20prs)

## Overview

 Adds no-for-in converter

Patch:
diff --git a/src/rules/converters.ts b/src/rules/converters.ts
index 782e17f..74f79cd 100644
--- a/src/rules/converters.ts
+++ b/src/rules/converters.ts
@@ -23,6 +23,7 @@ import { convertLabelPosition } from "./converters/label-position";
 import { convertLinebreakStyle } from "./converters/linebreak-style";
 import { convertMaxClassesPerFile } from "./converters/max-classes-per-file";
 import { convertMaxFileLineCount } from "./converters/max-file-line-count";
+import { convertMaxLineLength } from "./converters/max-line-length";
 import { convertMemberOrdering } from "./converters/member-ordering";
 import { convertNewlinePerChainedCall } from "./converters/newline-per-chained-call";
 import { convertNewParens } from "./converters/new-parens";
@@ -44,6 +45,7 @@ import { convertNoEmptyInterface } from "./converters/no-empty-interface";
 import { convertNoEval } from "./converters/no-eval";
 import { convertNoExplicitAny } from "./converters/no-explicit-any";
 import { convertNoFloatingPromises } from "./converters/no-floating-promises";
+import { convertNoForIn } from "./converters/no-for-in";
 import { convertNoForInArray } from "./converters/no-for-in-array";
 import { convertNoInferrableTypes } from "./converters/no-inferrable-types";
 import { convertNoInternalModule } from "./converters/no-internal-module";
@@ -79,6 +81,7 @@ import { convertNoUnsafeFinally } from "./converters/no-unsafe-finally";
 import { convertNoUseBeforeDeclare } from "./converters/no-use-before-declare";
 import { convertNoVarKeyword } from "./converters/no-var-keyword";
 import { convertNoVarRequires } from "./converters/no-var-requires";
+import { convertNoVoidExpression } from "./converters/no-void-expression";
 import { convertObjectLiteralKeyQuotes } from "./converters/object-literal-key-quotes";
 import { convertObjectLiteralShorthand } from "./converters/object-literal-shorthand";
 import { convertOneVariablePerDeclaration } from "./converters/one-variable-per-declaration";
@@ -100,6 +103,8 @@ import { convertUnifiedSignatures } from "./converters/unified-signatures";
 import { convertUnnecessaryBind } from "./converters/unnecessary-bind";
 import { convertUnnecessaryConstructor } from "./converters/unnecessary-constructor";
 import { convertUseIsnan } from "./converters/use-isnan";
+import { convertQuotemark } from "./converters/quotemark";
+import { convertTripleEquals } from "./converters/triple-equals";
 
 /**
  * Keys TSLint rule names to their ESLint rule converters.
@@ -138,6 +143,7 @@ export const converters = new Map([
     ["no-empty-interface", convertNoEmptyInterface],
     ["no-eval", convertNoEval],
     ["no-floating-promises", convertNoFloatingPromises],
+    ["no-for-in", convertNoForIn],
     ["no-for-in-array", convertNoForInArray],
     ["no-inferrable-types", convertNoInferrableTypes],
     ["no-internal-module", convertNoInternalModule],
@@ -165,6 +171,7 @@ export const converters = new Map([
     ["no-use-before-declare", convertNoUseBeforeDeclare],
     ["no-var-keyword", convertNoVarKeyword],
     ["no-var-requires", convertNoVarRequires],
+    ["no-void-expression", convertNoVoidExpression],
     ["prefer-for-of", convertPreferForOf],
     ["prefer-object-spread", convertPreferObjectSpread],
     ["promise-function-async", convertPromiseFunctionAsync],
@@ -184,6 +191,7 @@ export const converters = new Map([
     ["linebreak-style", convertLinebreakStyle],
     ["max-classes-per-file", convertMaxClassesPerFile],
     ["max-file-line-count", convertMaxFileLineCount],
+    ["max-line-length", convertMaxLineLength],
     ["no-consecutive-blank-lines", convertNoConsecutiveBlankLines],
     ["no-console", convertNoConsole],
     ["no-empty", convertNoEmpty],
@@ -207,6 +215,8 @@ export const converters = new Map([
     ["no-octal-literal", convertNoOctalLiteral],
     ["no-regex-spaces", convertNoRegexSpaces],
     ["no-unnecessary-semicolons", convertNoUnnecessarySemicolons],
+    ["quotemark", convertQuotemark],
+    ["triple-equals", convertTripleEquals],
 
     // These converters are all for rules that need more complex option conversions.
     // Some of them will likely need to have notices about changed lint behaviors...
@@ -216,22 +226,18 @@ export const converters = new Map([
     // TSLint core rules:
     // ["ban", convertBan], // no-restricted-properties
     // ["import-blacklist", convertImportBlacklist], // no-restricted-imports
-    // ["max-line-length", convertMaxLineLength], // max-len
     // ["newline-before-return", convertNewlineBeforeReturn],
     // ["no-duplicate-variable", convertNoDuplicateVariable], // no-redeclare
     // ["no-shadowed-variable", convertNoShadowedVariable], // no-shadow
     // ["no-trailing-whitespace", convertNoTrailingWhitespace], //  no-trailing-spaces
     // ["no-unused-expression", convertNoUnusedExpression], // no-unused-expressions
     // ["no-void-expression", convertNoVoidExpression], // (no exact equivalent)
-    // ["quotemark", convertQuotemark], // quotes
     // ["space-within-parens", convertSpaceWithinParens], // space-in-parens
-    // ["triple-equals", convertTripleEquals], // eqeqeq
     // ["variable-name", convertVariableName], // a bunch of rules...
 
     // tslint-microsoft-contrib rules:
     // ["max-func-body-length", convertMaxFuncBodyLength],
     // ["no-empty-line-after-opening-brace", convertNoEmptyLineAfterOpeningBrace], // padded-blocks
-    // ["no-for-in", convertNoForIn], // no-restricted-syntax config
     // ["no-function-expression", convertNoFunctionExpression], // ban-syntax config
     // ["no-suspicious-comment", convertNoSuspiciousComment],
     // ["no-with-statement", convertNoWithStatement],
diff --git a/src/rules/converters/max-line-length.ts b/src/rules/converters/max-line-length.ts
new file mode 100644
index 0000000..24ad4b1
--- /dev/null
+++ b/src/rules/converters/max-line-length.ts
@@ -0,0 +1,43 @@
+import { RuleConverter } from "../converter";
+
+export const convertMaxLineLength: RuleConverter = tslintRule => {
+    return {
+        rules: [
+            {
+                ...collectArguments(tslintRule.ruleArguments),
+                ruleName: "max-len",
+            },
+        ],
+    };
+};
+
+const collectArguments = (ruleArguments: any[]) => {
+    if (ruleArguments.length === 0 || ruleArguments[0] === false || ruleArguments.length < 2) {
+        return undefined;
+    }
+
+    if (ruleArguments.length === 2 && typeof ruleArguments[1] === "number") {
+        return {
+            ruleArguments: [
+                {
+                    code: ruleArguments[1],
+                },
+            ],
+        };
+    }
+
+    const argument = ruleArguments[1];
+
+    return {
+        ruleArguments: [
+            {
+                ...("limit" in argument && { code: argument.limit }),
+                ...("ignore-pattern" in argument && { ignorePattern: argument["ignore-pattern"] }),
+                ...("check-strings" in argument && { ignoreStrings: !argument["check-strings"] }),
+                ...("check-regex" in argument && {
+                    ignoreRegExpLiterals: !argument["check-regex"],
+                }),
+            },
+        ],
+    };
+};
diff --git a/src/rules/converters/no-for-in.ts b/src/rules/converters/no-for-in.ts
new file mode 100644
index 0000000..ed32259
--- /dev/null
+++ b/src/rules/converters/no-for-in.ts
@@ -0,0 +1,12 @@
+import { RuleConverter } from "../converter";
+
+export const convertNoForIn: RuleConverter = () => {
+    return {
+        rules: [
+            {
+                ruleArguments: ["ForInStatement"],
+                ruleName: "no-restricted-syntax",
+            },
+        ],
+    };
+};
diff --git a/src/rules/converters/no-void-expression.ts b/src/rules/converters/no-void-expression.ts
new file mode 100644
index 0000000..55a1467
--- /dev/null
+++ b/src/rules/converters/no-void-expression.ts
@@ -0,0 +1,15 @@
+import { RuleConverter } from "../converter";
+
+export const convertNoVoidExpression: RuleConverter = tslintRule => {
+    return {
+        rules: [
+            {
+                ...(tslintRule.ruleArguments.length > 0 &&
+                    tslintRule.ruleArguments.includes("ignore-arrow-function-shorthand") && {
+                        notices: ["ESLint does not support ignoring arrow function shorthand."],
+                    }),
+                ruleName: "no-void",
+            },
+        ],
+    };
+};
diff --git a/src/rules/converters/quotemark.ts b/src/rules/converters/quotemark.ts
new file mode 100644
index 0000000..3c864df
--- /dev/null
+++ b/src/rules/converters/quotemark.ts
@@ -0,0 +1,33 @@
+import { RuleConverter } from "../converter";
+
+type QuotemarkRule = string | { avoidEscape: true };
+export const convertQuotemark: RuleConverter = tslintRule => {
+    const notices: string[] = [];
+    const ruleArguments: QuotemarkRule[] = [];
+
+    ["jsx-single", "jsx-double", "avoid-template"].forEach(option => {
+        if (tslintRule.ruleArguments.includes(option)) {
+            notices.push(`Option "${option}" is not supported by ESLint.`);
+        }
+    });
+
+    ["single", "double", "backtick"].forEach(option => {
+        if (tslintRule.ruleArguments.includes(option)) {
+            ruleArguments.push(option);
+        }
+    });
+
+    if (tslintRule.ruleArguments.includes("avoid-escape")) {
+        ruleArguments.push({ avoidEscape: true });
+    }
+
+    return {
+        rules: [
+            {
+                notices,
+                ruleArguments,
+                ruleName: "@typescript-eslint/quotes",
+            },
+        ],
+    };
+};
diff --git a/src/rules/converters/triple-equals.ts b/src/rules/converters/triple-equals.ts
new file mode 100644
index 0000000..26c6975
--- /dev/null
+++ b/src/rules/converters/triple-equals.ts
@@ -0,0 +1,48 @@
+import { RuleConverter } from "../converter";
+
+export const convertTripleEquals: RuleConverter = tslintRule => {
+    const getRuleOptions = () => {
+        const smartOptionNotice =
+            'Option "smart" allows for comparing two literal values, evaluating the value of typeof and null comparisons.';
+
+        if (
+            tslintRule.ruleArguments.length !== 0 &&
+            tslintRule.ruleArguments[0] === "allow-null-check"
+        ) {
+            return {
+                arguments: ["smart"],
+                notices: [smartOptionNotice],
+            };
+        }
+
+        if (
+            tslintRule.ruleArguments.length !== 0 &&
+            tslintRule.ruleArguments[0] === "allow-undefined-check"
+        ) {
+            return {
+                arguments: ["smart"],
+                notices: [
+                    'Option "allow-undefined-check" is not supported by ESLint. Option "smart" is the closest.',
+                    smartOptionNotice,
+                ],
+            };
+        }
+
+        return {
+            arguments: ["always"],
+            notices: [],
+        };
+    };
+
+    const options = getRuleOptions();
+
+    return {
+        rules: [
+            {
+                ruleName: "eqeqeq",
+                ruleArguments: options.arguments,
+                notices: options.notices,
+            },
+        ],
+    };
+};


Test patch:
diff --git a/src/rules/converters/tests/max-line-length.test.ts b/src/rules/converters/tests/max-line-length.test.ts
new file mode 100644
index 0000000..17332a2
--- /dev/null
+++ b/src/rules/converters/tests/max-line-length.test.ts
@@ -0,0 +1,194 @@
+import { convertMaxLineLength } from "../max-line-length";
+
+describe(convertMaxLineLength, () => {
+    test("conversion without arguments", () => {
+        const result = convertMaxLineLength({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "max-len",
+                },
+            ],
+        });
+    });
+
+    test("conversion with one argument true value", () => {
+        const result = convertMaxLineLength({
+            ruleArguments: [true],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "max-len",
+                },
+            ],
+        });
+    });
+
+    test("conversion with two arguments and first is false", () => {
+        const result = convertMaxLineLength({
+            ruleArguments: [false, 123],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "max-len",
+                },
+            ],
+        });
+    });
+
+    test("conversion with two arguments and second is number", () => {
+        const result = convertMaxLineLength({
+            ruleArguments: [true, 123],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [{ code: 123 }],
+                    ruleName: "max-len",
+                },
+            ],
+        });
+    });
+
+    test("conversion with two arguments and second is object", () => {
+        const result = convertMaxLineLength({
+            ruleArguments: [
+                true,
+                {
+                    limit: 123,
+                    "ignore-pattern": "^import |^export {(.*?)}",
+                    "check-strings": true,
+                    "check-regex": true,
+                },
+            ],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [
+                        {
+                            code: 123,
+                            ignorePattern: "^import |^export {(.*?)}",
+                            ignoreStrings: false,
+                            ignoreRegExpLiterals: false,
+                        },
+                    ],
+                    ruleName: "max-len",
+                },
+            ],
+        });
+    });
+
+    test("conversion with check-strings inverting value true to false", () => {
+        const result = convertMaxLineLength({
+            ruleArguments: [
+                true,
+                {
+                    limit: 123,
+                    "check-strings": true,
+                },
+            ],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [
+                        {
+                            code: 123,
+                            ignoreStrings: false,
+                        },
+                    ],
+                    ruleName: "max-len",
+                },
+            ],
+        });
+    });
+
+    test("conversion with check-strings inverting value false to true", () => {
+        const result = convertMaxLineLength({
+            ruleArguments: [
+                true,
+                {
+                    limit: 123,
+                    "check-strings": false,
+                },
+            ],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [
+                        {
+                            code: 123,
+                            ignoreStrings: true,
+                        },
+                    ],
+                    ruleName: "max-len",
+                },
+            ],
+        });
+    });
+
+    test("conversion with check-regex inverting value true to false", () => {
+        const result = convertMaxLineLength({
+            ruleArguments: [
+                true,
+                {
+                    limit: 123,
+                    "check-regex": true,
+                },
+            ],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [
+                        {
+                            code: 123,
+                            ignoreRegExpLiterals: false,
+                        },
+                    ],
+                    ruleName: "max-len",
+                },
+            ],
+        });
+    });
+
+    test("conversion with check-regex inverting value false to true", () => {
+        const result = convertMaxLineLength({
+            ruleArguments: [
+                true,
+                {
+                    limit: 123,
+                    "check-regex": false,
+                },
+            ],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [
+                        {
+                            code: 123,
+                            ignoreRegExpLiterals: true,
+                        },
+                    ],
+                    ruleName: "max-len",
+                },
+            ],
+        });
+    });
+});
diff --git a/src/rules/converters/tests/no-for-in.test.ts b/src/rules/converters/tests/no-for-in.test.ts
new file mode 100644
index 0000000..af31320
--- /dev/null
+++ b/src/rules/converters/tests/no-for-in.test.ts
@@ -0,0 +1,18 @@
+import { convertNoForIn } from "../no-for-in";
+
+describe(convertNoForIn, () => {
+    test("conversion without arguments", () => {
+        const result = convertNoForIn({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "no-restricted-syntax",
+                    ruleArguments: ["ForInStatement"],
+                },
+            ],
+        });
+    });
+});
diff --git a/src/rules/converters/tests/no-void-expression.test.ts b/src/rules/converters/tests/no-void-expression.test.ts
new file mode 100644
index 0000000..84a03ad
--- /dev/null
+++ b/src/rules/converters/tests/no-void-expression.test.ts
@@ -0,0 +1,32 @@
+import { convertNoVoidExpression } from "../no-void-expression";
+
+describe(convertNoVoidExpression, () => {
+    test("conversion without arguments", () => {
+        const result = convertNoVoidExpression({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "no-void",
+                },
+            ],
+        });
+    });
+
+    test("conversion with ignore-arrow-function-shorthand argument", () => {
+        const result = convertNoVoidExpression({
+            ruleArguments: ["ignore-arrow-function-shorthand"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    notices: ["ESLint does not support ignoring arrow function shorthand."],
+                    ruleName: "no-void",
+                },
+            ],
+        });
+    });
+});
diff --git a/src/rules/converters/tests/quotemark.test.ts b/src/rules/converters/tests/quotemark.test.ts
new file mode 100644
index 0000000..a5aeb38
--- /dev/null
+++ b/src/rules/converters/tests/quotemark.test.ts
@@ -0,0 +1,87 @@
+import { convertQuotemark } from "../quotemark";
+
+describe(convertQuotemark, () => {
+    test("conversion without arguments", () => {
+        const result = convertQuotemark({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/quotes",
+                    ruleArguments: [],
+                    notices: [],
+                },
+            ],
+        });
+    });
+
+    test("conversion with an argument", () => {
+        const result = convertQuotemark({
+            ruleArguments: ["double"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/quotes",
+                    ruleArguments: ["double"],
+                    notices: [],
+                },
+            ],
+        });
+    });
+
+    test("conversion with an avoid-escape argument", () => {
+        const result = convertQuotemark({
+            ruleArguments: ["avoid-escape"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/quotes",
+                    ruleArguments: [{ avoidEscape: true }],
+                    notices: [],
+                },
+            ],
+        });
+    });
+
+    test("conversion with arguments", () => {
+        const result = convertQuotemark({
+            ruleArguments: ["single", "avoid-template"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/quotes",
+                    ruleArguments: ["single"],
+                    notices: ['Option "avoid-template" is not supported by ESLint.'],
+                },
+            ],
+        });
+    });
+
+    test("conversion with unsupported arguments", () => {
+        const result = convertQuotemark({
+            ruleArguments: ["jsx-single", "jsx-double", "avoid-template"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleName: "@typescript-eslint/quotes",
+                    ruleArguments: [],
+                    notices: [
+                        'Option "jsx-single" is not supported by ESLint.',
+                        'Option "jsx-double" is not supported by ESLint.',
+                        'Option "avoid-template" is not supported by ESLint.',
+                    ],
+                },
+            ],
+        });
+    });
+});
diff --git a/src/rules/converters/tests/triple-equals.test.ts b/src/rules/converters/tests/triple-equals.test.ts
new file mode 100644
index 0000000..ad2940b
--- /dev/null
+++ b/src/rules/converters/tests/triple-equals.test.ts
@@ -0,0 +1,56 @@
+import { convertTripleEquals } from "../triple-equals";
+
+describe(convertTripleEquals, () => {
+    test("conversion without arguments", () => {
+        const result = convertTripleEquals({
+            ruleArguments: [],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: ["always"],
+                    ruleName: "eqeqeq",
+                    notices: [],
+                },
+            ],
+        });
+    });
+
+    test("conversion with an allow-null-check argument", () => {
+        const result = convertTripleEquals({
+            ruleArguments: ["allow-null-check"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: ["smart"],
+                    ruleName: "eqeqeq",
+                    notices: [
+                        'Option "smart" allows for comparing two literal values, evaluating the value of typeof and null comparisons.',
+                    ],
+                },
+            ],
+        });
+    });
+
+    test("conversion with an allow-undefined-check argument", () => {
+        const result = convertTripleEquals({
+            ruleArguments: ["allow-undefined-check"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: ["smart"],
+                    ruleName: "eqeqeq",
+                    notices: [
+                        'Option "allow-undefined-check" is not supported by ESLint. Option "smart" is the closest.',
+                        'Option "smart" allows for comparing two literal values, evaluating the value of typeof and null comparisons.',
+                    ],
+                },
+            ],
+        });
+    });
+});