Repository: nzakas/eslint-plugin-typescript
Pull request #124 (2018-03-26 22:15:16)

add no-inferrable-types rule
Related to #5.

Seems all fine, but I only discovered this repo today (and eslint's internals) so do let me know if anything's wrong or could be written better.

43081j: Yup, will add it to the docs and tests tomorrow.

We only check assignments so normal type annotations get through fine.

On Mon, 26 Mar 2018, 23:31 Jed Fox, <notifications@github.com> wrote:

> *@j-f1* commented on this pull request.
> ------------------------------
>
> In docs/rules/no-inferrable-types.md
> <https://github.com/nzakas/eslint-plugin-typescript/pull/124#discussion_r177255065>
> :
>
> > +```
> +
> +### Default
> +
> +When none of the options are truthy, the following patterns are valid:
> +
> +```ts
> +const foo = 5;
> +const bar = true;
> +const baz = 'str';
> +
> +class Foo {
> +    prop = 5;
> +}
> +
> +function fn(a = 5, b = true) {
>
> Is function fn(a: number, b: string) {} valid?
>
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/nzakas/eslint-plugin-typescript/pull/124#pullrequestreview-107096581>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AFagYS8oE5qa9xH6L4mMe661MEtTFTD6ks5tiWxBgaJpZM4S7_eh>
> .
>


Patch:
diff --git a/README.md b/README.md
index ccc1723..6a6d9ae 100644
--- a/README.md
+++ b/README.md
@@ -63,6 +63,7 @@ Then configure the rules you want to use under the rules section.
 * [`typescript/no-array-constructor`](./docs/rules/no-array-constructor.md) — Disallow generic `Array` constructors
 * [`typescript/no-empty-interface`](./docs/rules/no-empty-interface.md) — Disallow the declaration of empty interfaces (`no-empty-interface` from TSLint)
 * [`typescript/no-explicit-any`](./docs/rules/no-explicit-any.md) — Disallow usage of the `any` type (`no-any` from TSLint)
+* [`typescript/no-inferrable-types`](./docs/rules/no-inferrable-types.md) — Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean. (`no-inferrable-types` from TSLint)
 * [`typescript/no-namespace`](./docs/rules/no-namespace.md) — Disallow the use of custom TypeScript modules and namespaces
 * [`typescript/no-non-null-assertion`](./docs/rules/no-non-null-assertion.md) — Disallows non-null assertions using the `!` postfix operator (`no-non-null-assertion` from TSLint)
 * [`typescript/no-parameter-properties`](./docs/rules/no-parameter-properties.md) — Disallow the use of parameter properties in class constructors. (`no-parameter-properties` from TSLint)
@@ -70,6 +71,7 @@ Then configure the rules you want to use under the rules section.
 * [`typescript/no-type-alias`](./docs/rules/no-type-alias.md) — Disallow the use of type aliases (`interface-over-type-literal` from TSLint)
 * [`typescript/no-unused-vars`](./docs/rules/no-unused-vars.md) — Prevent TypeScript-specific constructs from being erroneously flagged as unused
 * [`typescript/no-use-before-define`](./docs/rules/no-use-before-define.md) — Disallow the use of variables before they are defined
+* [`typescript/no-var-requires`](./docs/rules/no-var-requires.md) — Disallows the use of require statements except in import statements (`no-var-requires` from TSLint)
 * [`typescript/prefer-namespace-keyword`](./docs/rules/prefer-namespace-keyword.md) — Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules. (`no-internal-module` from TSLint)
 * [`typescript/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) — Require consistent spacing around type annotations
 <!-- end rule list -->
diff --git a/docs/rules/no-inferrable-types.md b/docs/rules/no-inferrable-types.md
new file mode 100644
index 0000000..7180c49
--- /dev/null
+++ b/docs/rules/no-inferrable-types.md
@@ -0,0 +1,86 @@
+# Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean. (no-inferrable-types)
+
+Explicit types where they can be easily inferred may add unnecessary verbosity.
+
+## Rule Details
+
+This rule disallows explicit type declarations on parameters, variables
+and properties where the type can be easily inferred from its value.
+
+## Options
+
+This rule has an options object:
+
+```json
+{
+    "ignoreProperties": false,
+    "ignoreParameters": false
+}
+```
+
+### Default
+
+When none of the options are truthy, the following patterns are valid:
+
+```ts
+const foo = 5;
+const bar = true;
+const baz = 'str';
+
+class Foo {
+    prop = 5;
+}
+
+function fn(a = 5, b = true) {
+}
+
+function fn(a: number, b: boolean, c: string) {
+}
+```
+
+The following are invalid:
+
+```ts
+const foo: number = 5;
+const bar: boolean = true;
+const baz: string = 'str';
+
+class Foo {
+    prop: number = 5;
+}
+
+function fn(a: number = 5, b: boolean = true) {
+}
+```
+
+### `ignoreProperties`
+
+When set to true, the following pattern is considered valid:
+
+```ts
+class Foo {
+    prop: number = 5;
+}
+```
+
+### `ignoreParameters`
+
+When set to true, the following pattern is considered valid:
+
+```ts
+function foo(a: number = 5, b: boolean = true) {
+    // ...
+}
+```
+
+## When Not To Use It
+
+If you do not want to enforce inferred types.
+
+## Further Reading
+
+TypeScript [Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html)
+
+## Compatibility
+
+TSLint: [no-inferrable-types](https://palantir.github.io/tslint/rules/no-inferrable-types/)
diff --git a/docs/rules/no-var-requires.md b/docs/rules/no-var-requires.md
new file mode 100644
index 0000000..d376176
--- /dev/null
+++ b/docs/rules/no-var-requires.md
@@ -0,0 +1,28 @@
+# Disallows the use of require statements except in import statements (no-var-requires)
+
+In other words, the use of forms such as `var foo = require("foo")` are banned. Instead use ES6 style imports or `import foo = require("foo")` imports.
+
+## Rule Details
+
+Examples of **incorrect** code for this rule:
+
+```ts
+var foo = require("foo");
+const foo = require("foo");
+let foo = require("foo");
+```
+
+Examples of **correct** code for this rule:
+
+```ts
+import foo = require("foo");
+require("foo");
+```
+
+## When Not To Use It
+
+If you don't care about TypeScript module syntax, then you will not need this rule.
+
+## Compatibility
+
+* TSLint: [no-var-requires](https://palantir.github.io/tslint/rules/no-var-requires/)
diff --git a/lib/rules/member-delimiter-style.js b/lib/rules/member-delimiter-style.js
index 854f99f..8e2eb10 100644
--- a/lib/rules/member-delimiter-style.js
+++ b/lib/rules/member-delimiter-style.js
@@ -90,6 +90,7 @@ module.exports = {
          */
         function checkLastToken(member, opts, isLast, isSameLine) {
             let message;
+            let missingDelimiter = false;
             const lastToken = sourceCode.getLastToken(member, {
                 includeComments: false
             });
@@ -118,6 +119,7 @@ module.exports = {
                 }
 
                 if (!canOmit) {
+                    missingDelimiter = true;
                     message =
                         opts.delimiter === "semi"
                             ? "Expected a semicolon."
@@ -138,7 +140,29 @@ module.exports = {
                             column: lastToken.loc.end.column
                         }
                     },
-                    message
+                    message,
+                    fix(fixer) {
+                        let token;
+
+                        if (opts.delimiter === "semi") {
+                            token = ";";
+                        } else if (opts.delimiter === "comma") {
+                            token = ",";
+                        } else {
+                            // remove the unneeded token
+                            return fixer.remove(lastToken);
+                        }
+
+                        if (missingDelimiter) {
+                            // add the missing delimiter
+                            return fixer.insertTextAfter(lastToken, token);
+                        } else if (isLast && !opts.requireLast) {
+                            return fixer.remove(lastToken);
+                        }
+
+                        // correct the current delimiter
+                        return fixer.replaceText(lastToken, token);
+                    }
                 });
             }
         }
diff --git a/lib/rules/member-ordering.js b/lib/rules/member-ordering.js
index 0785e5b..28e0513 100644
--- a/lib/rules/member-ordering.js
+++ b/lib/rules/member-ordering.js
@@ -172,23 +172,9 @@ module.exports = {
          */
         function shouldBeProcessedAsMethod(node) {
             // check for bound methods in ClassProperty nodes.
-            if (
-                node.value &&
-                functionExpressions.indexOf(node.value.type) > -1
-            ) {
-                return true;
-            }
-
-            // check for bound methods in TSPropertySignature nodes.
-            if (
-                node.typeAnnotation &&
-                node.typeAnnotation.typeAnnotation &&
-                node.typeAnnotation.typeAnnotation.type === "TSFunctionType"
-            ) {
-                return true;
-            }
-
-            return false;
+            return (
+                node.value && functionExpressions.indexOf(node.value.type) > -1
+            );
         }
 
         /**
diff --git a/lib/rules/no-inferrable-types.js b/lib/rules/no-inferrable-types.js
new file mode 100644
index 0000000..0598a6b
--- /dev/null
+++ b/lib/rules/no-inferrable-types.js
@@ -0,0 +1,191 @@
+/**
+ * @fileoverview Disallows explicit type declarations for inferrable types
+ * @author James Garbutt <https://github.com/43081j>
+ */
+
+"use strict";
+
+const util = require("../util");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        docs: {
+            description:
+                "Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean.",
+            extraDescription: [util.tslintRule("no-inferrable-types")],
+            category: "TypeScript",
+            url:
+                "https://github.com/nzakas/eslint-plugin-typescript/blob/master/docs/rules/no-inferrable-types.md"
+        },
+        fixable: "code",
+        schema: [
+            {
+                type: "object",
+                properties: {
+                    ignoreParameters: {
+                        type: "boolean"
+                    },
+                    ignoreProperties: {
+                        type: "boolean"
+                    }
+                },
+                additionalProperties: false
+            }
+        ]
+    },
+
+    create(context) {
+        const ignoreParameters = context.options[0]
+            ? context.options[0].ignoreParameters
+            : false;
+        const ignoreProperties = context.options[0]
+            ? context.options[0].ignoreProperties
+            : false;
+
+        /**
+         * Returns whether a node has an inferrable value or not
+         * @param {ASTNode} node the node to check
+         * @param {ASTNode} init the initializer
+         * @returns {boolean} whether the node has an inferrable type
+         */
+        function isInferrable(node, init) {
+            if (node.type !== "TSTypeAnnotation" || !node.typeAnnotation) {
+                return false;
+            }
+
+            if (!init) {
+                return false;
+            }
+
+            const annotation = node.typeAnnotation;
+
+            if (annotation.type === "TSStringKeyword") {
+                return (
+                    (init.type === "Literal" &&
+                        typeof init.value === "string") ||
+                    (init.type === "TemplateElement" &&
+                        (!init.expressions || init.expressions.length === 0))
+                );
+            }
+
+            if (annotation.type === "TSBooleanKeyword") {
+                return init.type === "Literal";
+            }
+
+            if (annotation.type === "TSNumberKeyword") {
+                // Infinity is special
+                if (
+                    (init.type === "UnaryExpression" &&
+                        init.operator === "-" &&
+                        init.argument.type === "Identifier" &&
+                        init.argument.name === "Infinity") ||
+                    (init.type === "Identifier" && init.name === "Infinity")
+                ) {
+                    return true;
+                }
+
+                return (
+                    init.type === "Literal" && typeof init.value === "number"
+                );
+            }
+
+            return false;
+        }
+
+        /**
+         * Reports an inferrable type declaration, if any
+         * @param {ASTNode} node the node being visited
+         * @param {ASTNode} typeNode the type annotation node
+         * @param {ASTNode} initNode the initializer node
+         * @returns {void}
+         */
+        function reportInferrableType(node, typeNode, initNode) {
+            if (!typeNode || !initNode || !typeNode.typeAnnotation) {
+                return;
+            }
+
+            if (!isInferrable(typeNode, initNode)) {
+                return;
+            }
+
+            const typeMap = {
+                TSBooleanKeyword: "boolean",
+                TSNumberKeyword: "number",
+                TSStringKeyword: "string"
+            };
+
+            const type = typeMap[typeNode.typeAnnotation.type];
+
+            context.report({
+                node,
+                message: `Type ${type} trivially inferred from a ${type} literal, remove type annotation`,
+                fix: fixer => fixer.remove(typeNode)
+            });
+        }
+
+        /**
+         * Visits variables
+         * @param {ASTNode} node the node to be visited
+         * @returns {void}
+         */
+        function inferrableVariableVisitor(node) {
+            if (!node.id) {
+                return;
+            }
+            reportInferrableType(node, node.id.typeAnnotation, node.init);
+        }
+
+        /**
+         * Visits parameters
+         * @param {ASTNode} node the node to be visited
+         * @returns {void}
+         */
+        function inferrableParameterVisitor(node) {
+            if (ignoreParameters || !node.params) {
+                return;
+            }
+            node.params
+                .filter(
+                    param =>
+                        param.type === "AssignmentPattern" &&
+                        param.left &&
+                        param.right
+                )
+                .forEach(param => {
+                    reportInferrableType(
+                        param,
+                        param.left.typeAnnotation,
+                        param.right
+                    );
+                });
+        }
+
+        /**
+         * Visits properties
+         * @param {ASTNode} node the node to be visited
+         * @returns {void}
+         */
+        function inferrablePropertyVisitor(node) {
+            // We ignore `readonly` because of Microsoft/TypeScript#14416
+            // Essentially a readonly property without a type
+            // will result in its value being the type, leading to
+            // compile errors if the type is stripped.
+            if (ignoreProperties || node.readonly) {
+                return;
+            }
+            reportInferrableType(node, node.typeAnnotation, node.value);
+        }
+
+        return {
+            VariableDeclarator: inferrableVariableVisitor,
+            FunctionExpression: inferrableParameterVisitor,
+            FunctionDeclaration: inferrableParameterVisitor,
+            ArrowFunctionExpression: inferrableParameterVisitor,
+            ClassProperty: inferrablePropertyVisitor
+        };
+    }
+};
diff --git a/lib/rules/no-var-requires.js b/lib/rules/no-var-requires.js
new file mode 100644
index 0000000..d4e6592
--- /dev/null
+++ b/lib/rules/no-var-requires.js
@@ -0,0 +1,45 @@
+/**
+ * @fileoverview Disallows the use of require statements except in import statements.
+ * @author Macklin Underdown
+ */
+"use strict";
+
+const util = require("../util");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        docs: {
+            description:
+                "Disallows the use of require statements except in import statements",
+            extraDescription: [util.tslintRule("no-var-requires")],
+            category: "TypeScript",
+            url:
+                "https://github.com/nzakas/eslint-plugin-typescript/blob/master/docs/rules/no-var-requires.md"
+        },
+        schema: []
+    },
+    create(context) {
+        //----------------------------------------------------------------------
+        // Public
+        //----------------------------------------------------------------------
+
+        return {
+            CallExpression(node) {
+                if (
+                    node.callee.name === "require" &&
+                    node.parent.type === "VariableDeclarator"
+                ) {
+                    context.report({
+                        node,
+                        message:
+                            "Require statement not part of import statement"
+                    });
+                }
+            }
+        };
+    }
+};
diff --git a/lib/rules/type-annotation-spacing.js b/lib/rules/type-annotation-spacing.js
index 314e694..f4c956c 100644
--- a/lib/rules/type-annotation-spacing.js
+++ b/lib/rules/type-annotation-spacing.js
@@ -79,38 +79,46 @@ module.exports = {
          */
         function checkTypeAnnotationSpacing(typeAnnotation) {
             const nextToken = typeAnnotation;
-            const punctuatorToken = sourceCode.getTokenBefore(nextToken);
-            const previousToken = sourceCode.getTokenBefore(punctuatorToken);
-            const type = punctuatorToken.value;
+            const punctuatorTokenEnd = sourceCode.getTokenBefore(nextToken);
+            let punctuatorTokenStart = punctuatorTokenEnd;
+            let previousToken = sourceCode.getTokenBefore(punctuatorTokenEnd);
+            let type = punctuatorTokenEnd.value;
 
             if (punctuators.indexOf(type) === -1) {
                 return;
             }
 
-            const previousDelta =
-                punctuatorToken.range[0] - previousToken.range[1];
-            const nextDelta = nextToken.range[0] - punctuatorToken.range[1];
-
             const before =
                 type === ":" ? colonOptions.before : arrowOptions.before;
             const after =
                 type === ":" ? colonOptions.after : arrowOptions.after;
 
+            if (type === ":" && previousToken.value === "?") {
+                // shift the start to the ?
+                type = "?:";
+                punctuatorTokenStart = previousToken;
+                previousToken = sourceCode.getTokenBefore(previousToken);
+            }
+
+            const previousDelta =
+                punctuatorTokenStart.range[0] - previousToken.range[1];
+            const nextDelta = nextToken.range[0] - punctuatorTokenEnd.range[1];
+
             if (after && nextDelta === 0) {
                 context.report({
-                    node: punctuatorToken,
+                    node: punctuatorTokenEnd,
                     message: `Expected a space after the '${type}'`,
                     fix(fixer) {
-                        return fixer.insertTextAfter(punctuatorToken, " ");
+                        return fixer.insertTextAfter(punctuatorTokenEnd, " ");
                     }
                 });
             } else if (!after && nextDelta > 0) {
                 context.report({
-                    node: punctuatorToken,
+                    node: punctuatorTokenEnd,
                     message: `Unexpected space after the '${type}'`,
                     fix(fixer) {
                         return fixer.removeRange([
-                            punctuatorToken.range[1],
+                            punctuatorTokenEnd.range[1],
                             nextToken.range[0]
                         ]);
                     }
@@ -119,7 +127,7 @@ module.exports = {
 
             if (before && previousDelta === 0) {
                 context.report({
-                    node: punctuatorToken,
+                    node: punctuatorTokenStart,
                     message: `Expected a space before the '${type}'`,
                     fix(fixer) {
                         return fixer.insertTextAfter(previousToken, " ");
@@ -127,12 +135,12 @@ module.exports = {
                 });
             } else if (!before && previousDelta > 0) {
                 context.report({
-                    node: punctuatorToken,
+                    node: punctuatorTokenStart,
                     message: `Unexpected space before the '${type}'`,
                     fix(fixer) {
                         return fixer.removeRange([
                             previousToken.range[1],
-                            punctuatorToken.range[0]
+                            punctuatorTokenStart.range[0]
                         ]);
                     }
                 });
diff --git a/package.json b/package.json
index 7fcb0e3..d4b4536 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-typescript",
-  "version": "0.10.0",
+  "version": "0.11.0",
   "description": "TypeScript plugin for ESLint",
   "keywords": [
     "eslint",
@@ -27,12 +27,12 @@
     "eslint-config-eslint": "^4.0.0",
     "eslint-config-prettier": "^2.9.0",
     "eslint-docs": "^0.1.1",
-    "eslint-plugin-node": "^5.1.1",
+    "eslint-plugin-node": "^6.0.1",
     "eslint-plugin-prettier": "^2.2.0",
     "husky": "^0.14.3",
     "lint-staged": "^6.0.0",
     "mocha": "^4.0.1",
-    "prettier": "1.9.1",
+    "prettier": "^1.11.1",
     "typescript": "~2.6.1",
     "typescript-eslint-parser": "^10.0.0"
   },


Test patch:
diff --git a/tests/lib/rules/member-delimiter-style.js b/tests/lib/rules/member-delimiter-style.js
index 4fa2ef1..9463d52 100644
--- a/tests/lib/rules/member-delimiter-style.js
+++ b/tests/lib/rules/member-delimiter-style.js
@@ -723,6 +723,12 @@ type Bar = {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string;
+    age: number;
 }
             `,
             errors: [
@@ -743,6 +749,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string;
+    age: number;
 }
             `,
             options: [{ delimiter: "semi" }],
@@ -764,6 +776,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string;
+    age: number;
 }
             `,
             options: [{ delimiter: "semi", requireLast: true }],
@@ -782,9 +800,37 @@ interface Foo {
         },
         {
             code: `
+interface Foo {
+    name: string
+    age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string;
+    age: number
+}
+            `,
+            options: [{ delimiter: "semi", requireLast: false }],
+            errors: [
+                {
+                    message: "Expected a semicolon.",
+                    line: 3,
+                    column: 17
+                }
+            ]
+        },
+        {
+            code: `
 interface Foo {
     name: string;
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string;
+    age: number;
 }
             `,
             options: [{ delimiter: "semi", requireLast: true }],
@@ -801,6 +847,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number,
 }
             `,
             options: [{ delimiter: "comma" }],
@@ -822,6 +874,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number,
 }
             `,
             options: [{ delimiter: "comma", requireLast: true }],
@@ -843,6 +901,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number
 }
             `,
             options: [{ delimiter: "comma", requireLast: false }],
@@ -859,6 +923,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number,
 }
             `,
             options: [{ delimiter: "comma" }],
@@ -880,6 +950,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number,
 }
             `,
             options: [{ delimiter: "comma", requireLast: true }],
@@ -901,6 +977,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number
 }
             `,
             options: [{ delimiter: "comma", requireLast: false }],
@@ -917,6 +999,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number
 }
             `,
             options: [{ delimiter: "comma", requireLast: false }],
@@ -938,6 +1026,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none" }],
@@ -959,6 +1053,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: true }],
@@ -980,6 +1080,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: false }],
@@ -996,6 +1102,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: false }],
@@ -1012,6 +1124,12 @@ interface Foo {
 interface Foo {
     name: string,
     age: number,
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none" }],
@@ -1033,6 +1151,12 @@ interface Foo {
 interface Foo {
     name: string,
     age: number,
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: true }],
@@ -1054,6 +1178,12 @@ interface Foo {
 interface Foo {
     name: string,
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: false }],
@@ -1070,6 +1200,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number,
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: false }],
@@ -1086,6 +1222,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string;
+    age: number;
 }
             `,
             options: [
@@ -1112,6 +1254,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string;
+    age: number;
 }
             `,
             options: [
@@ -1141,6 +1289,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string;
+    age: number;
 }
             `,
             options: [
@@ -1165,6 +1319,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number,
 }
             `,
             options: [
@@ -1191,6 +1351,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number,
 }
             `,
             options: [
@@ -1220,6 +1386,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number
 }
             `,
             options: [
@@ -1244,6 +1416,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number,
 }
             `,
             options: [
@@ -1270,6 +1448,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number,
 }
             `,
             options: [
@@ -1299,6 +1483,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number
 }
             `,
             options: [
@@ -1323,6 +1513,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number
 }
             `,
             options: [
@@ -1352,6 +1548,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -1378,6 +1580,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -1407,6 +1615,12 @@ interface Foo {
 interface Foo {
     name: string;
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -1431,6 +1645,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number;
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -1455,6 +1675,12 @@ interface Foo {
 interface Foo {
     name: string,
     age: number,
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -1481,6 +1707,12 @@ interface Foo {
 interface Foo {
     name: string,
     age: number,
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -1510,6 +1742,12 @@ interface Foo {
 interface Foo {
     name: string,
     age: number
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -1534,6 +1772,12 @@ interface Foo {
 interface Foo {
     name: string
     age: number,
+}
+            `,
+            output: `
+interface Foo {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -1558,6 +1802,12 @@ interface Foo {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string;
+    age: number;
 }
             `,
             errors: [
@@ -1578,6 +1828,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string;
+    age: number;
 }
             `,
             options: [{ delimiter: "semi" }],
@@ -1599,6 +1855,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string;
+    age: number;
 }
             `,
             options: [{ delimiter: "semi", requireLast: true }],
@@ -1620,6 +1882,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string;
+    age: number;
 }
             `,
             options: [{ delimiter: "semi", requireLast: true }],
@@ -1636,6 +1904,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number,
 }
             `,
             options: [{ delimiter: "comma" }],
@@ -1657,6 +1931,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number,
 }
             `,
             options: [{ delimiter: "comma", requireLast: true }],
@@ -1678,6 +1958,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number
 }
             `,
             options: [{ delimiter: "comma", requireLast: false }],
@@ -1694,6 +1980,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number,
 }
             `,
             options: [{ delimiter: "comma" }],
@@ -1715,6 +2007,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number,
 }
             `,
             options: [{ delimiter: "comma", requireLast: true }],
@@ -1736,6 +2034,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number
 }
             `,
             options: [{ delimiter: "comma", requireLast: false }],
@@ -1752,6 +2056,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number
 }
             `,
             options: [{ delimiter: "comma", requireLast: false }],
@@ -1773,6 +2083,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none" }],
@@ -1794,6 +2110,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: true }],
@@ -1815,6 +2137,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: false }],
@@ -1831,6 +2159,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: false }],
@@ -1847,6 +2181,12 @@ type Foo = {
 type Foo = {
     name: string,
     age: number,
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none" }],
@@ -1868,6 +2208,12 @@ type Foo = {
 type Foo = {
     name: string,
     age: number,
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: true }],
@@ -1889,6 +2235,12 @@ type Foo = {
 type Foo = {
     name: string,
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: false }],
@@ -1905,6 +2257,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number,
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [{ delimiter: "none", requireLast: false }],
@@ -1921,6 +2279,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string;
+    age: number;
 }
             `,
             options: [
@@ -1947,6 +2311,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string;
+    age: number;
 }
             `,
             options: [
@@ -1976,6 +2346,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string;
+    age: number;
 }
             `,
             options: [
@@ -2000,6 +2376,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number,
 }
             `,
             options: [
@@ -2026,6 +2408,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number,
 }
             `,
             options: [
@@ -2055,6 +2443,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number
 }
             `,
             options: [
@@ -2079,6 +2473,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number,
 }
             `,
             options: [
@@ -2105,6 +2505,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number,
 }
             `,
             options: [
@@ -2134,6 +2540,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number
 }
             `,
             options: [
@@ -2158,6 +2570,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string,
+    age: number
 }
             `,
             options: [
@@ -2187,6 +2605,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -2213,6 +2637,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -2242,6 +2672,12 @@ type Foo = {
 type Foo = {
     name: string;
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -2266,6 +2702,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number;
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -2290,6 +2732,12 @@ type Foo = {
 type Foo = {
     name: string,
     age: number,
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -2316,6 +2764,12 @@ type Foo = {
 type Foo = {
     name: string,
     age: number,
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -2345,6 +2799,12 @@ type Foo = {
 type Foo = {
     name: string,
     age: number
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -2369,6 +2829,12 @@ type Foo = {
 type Foo = {
     name: string
     age: number,
+}
+            `,
+            output: `
+type Foo = {
+    name: string
+    age: number
 }
             `,
             options: [
@@ -2398,6 +2864,17 @@ interface Foo {
 type Bar = {
     name: string,
     age: number,
+}
+            `,
+            output: `
+interface Foo {
+    name: string,
+    age: number,
+}
+
+type Bar = {
+    name: string;
+    age: number;
 }
             `,
             options: [
@@ -2437,6 +2914,11 @@ type Bar = {
             code: `
 interface Foo {
     [key: string]: any
+}
+            `,
+            output: `
+interface Foo {
+    [key: string]: any;
 }
             `,
             errors: [
@@ -2451,6 +2933,11 @@ interface Foo {
             code: `
 interface Foo {
     [key: string]: any
+}
+            `,
+            output: `
+interface Foo {
+    [key: string]: any;
 }
             `,
             options: [{ ignoreSingleLine: true }],
@@ -2464,6 +2951,7 @@ interface Foo {
         },
         {
             code: "interface Foo { [key: string]: any }",
+            output: "interface Foo { [key: string]: any; }",
             options: [{ ignoreSingleLine: false }],
             errors: [
                 {
@@ -2475,6 +2963,7 @@ interface Foo {
         },
         {
             code: "interface Foo { [key: string]: any }",
+            output: "interface Foo { [key: string]: any; }",
             options: [
                 {
                     requireLast: true,
@@ -2491,6 +2980,7 @@ interface Foo {
         },
         {
             code: "interface Foo { [key: string]: any }",
+            output: "interface Foo { [key: string]: any; }",
             options: [
                 {
                     requireLast: true,
@@ -2512,6 +3002,11 @@ interface Foo {
             code: `
 type Foo = {
     [key: string]: any
+}
+            `,
+            output: `
+type Foo = {
+    [key: string]: any;
 }
             `,
             errors: [
@@ -2526,6 +3021,11 @@ type Foo = {
             code: `
 type Foo = {
     [key: string]: any
+}
+            `,
+            output: `
+type Foo = {
+    [key: string]: any;
 }
             `,
             options: [{ ignoreSingleLine: true }],
@@ -2539,6 +3039,7 @@ type Foo = {
         },
         {
             code: "type Foo = { [key: string]: any }",
+            output: "type Foo = { [key: string]: any; }",
             options: [{ ignoreSingleLine: false }],
             errors: [
                 {
@@ -2550,6 +3051,7 @@ type Foo = {
         },
         {
             code: "type Foo = { [key: string]: any }",
+            output: "type Foo = { [key: string]: any; }",
             options: [
                 {
                     requireLast: true,
@@ -2566,6 +3068,7 @@ type Foo = {
         },
         {
             code: "type Foo = { [key: string]: any }",
+            output: "type Foo = { [key: string]: any; }",
             options: [
                 {
                     requireLast: true,
diff --git a/tests/lib/rules/member-ordering.js b/tests/lib/rules/member-ordering.js
index ea23bcc..1fee670 100644
--- a/tests/lib/rules/member-ordering.js
+++ b/tests/lib/rules/member-ordering.js
@@ -540,6 +540,21 @@ class Foo {
         },
         {
             code: `
+class Foo {
+    private required: boolean;
+    private typeChecker: (data: any) => boolean;
+    constructor(validator: (data: any) => boolean) {
+        this.typeChecker = validator;
+    }
+    check(data: any): boolean {
+        return this.typeChecker(data);
+    }
+}
+            `,
+            options: [{ classes: ["field", "constructor", "method"] }]
+        },
+        {
+            code: `
 class Foo {
     public static G() {}
     protected static H() {}
@@ -1184,9 +1199,9 @@ class Foo {
 class Foo {
     J() {}
     K = () => {}
-    L: () => {}
     constructor () {}
     A: string;
+    L: () => {}
 }
             `,
             options: [{ default: ["method", "constructor", "field"] }]
@@ -1194,8 +1209,8 @@ class Foo {
         `
 interface Foo {
     A: string;
+    K: () => {};
     J();
-    K: () => {}
 }
         `,
         {
@@ -1211,8 +1226,8 @@ interface Foo {
         `
 type Foo = {
     A: string;
-    J();
     K: () => {}
+    J();
 }
         `,
         {
@@ -2903,12 +2918,6 @@ class Foo {
                         "Member K should be declared before all constructor definitions.",
                     line: 5,
                     column: 5
-                },
-                {
-                    message:
-                        "Member L should be declared before all constructor definitions.",
-                    line: 6,
-                    column: 5
                 }
             ]
         },
@@ -2916,32 +2925,14 @@ class Foo {
             code: `
 interface Foo {
     K: () => {}
-    A: string;
-    J();
-}
-            `,
-            errors: [
-                {
-                    message:
-                        "Member A should be declared before all method definitions.",
-                    line: 4,
-                    column: 5
-                }
-            ]
-        },
-        {
-            code: `
-interface Foo {
     J();
     A: string;
-    K: () => {}
 }
             `,
-            options: [{ default: ["method", "constructor", "field"] }],
             errors: [
                 {
                     message:
-                        "Member K should be declared before all field definitions.",
+                        "Member A should be declared before all method definitions.",
                     line: 5,
                     column: 5
                 }
@@ -2951,15 +2942,15 @@ interface Foo {
             code: `
 type Foo = {
     K: () => {}
-    A: string;
     J();
+    A: string;
 }
             `,
             errors: [
                 {
                     message:
                         "Member A should be declared before all method definitions.",
-                    line: 4,
+                    line: 5,
                     column: 5
                 }
             ]
@@ -2967,16 +2958,16 @@ type Foo = {
         {
             code: `
 type Foo = {
-    J();
     A: string;
     K: () => {}
+    J();
 }
             `,
             options: [{ default: ["method", "constructor", "field"] }],
             errors: [
                 {
                     message:
-                        "Member K should be declared before all field definitions.",
+                        "Member J should be declared before all field definitions.",
                     line: 5,
                     column: 5
                 }
diff --git a/tests/lib/rules/no-inferrable-types.js b/tests/lib/rules/no-inferrable-types.js
new file mode 100644
index 0000000..c6473db
--- /dev/null
+++ b/tests/lib/rules/no-inferrable-types.js
@@ -0,0 +1,161 @@
+/**
+ * @fileoverview Disallows explicit type declarations for inferrable types
+ * @author James Garbutt <https://github.com/43081j>
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const rule = require("../../../lib/rules/no-inferrable-types");
+const RuleTester = require("eslint").RuleTester;
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+    parser: "typescript-eslint-parser"
+});
+
+ruleTester.run("no-inferrable-types", rule, {
+    valid: [
+        "const a = 5",
+        "const a = true",
+        "const a = 'foo'",
+
+        "const fn = (a = 5, b = true, c = 'foo') => {}",
+        "const fn = function(a = 5, b = true, c = 'foo') {}",
+        "function fn(a = 5, b = true, c = 'foo') {}",
+        "function fn(a: number, b: boolean, c: string) {}",
+
+        "class Foo { a = 5; b = true; c = 'foo'; }",
+        "class Foo { readonly a: number = 5; }",
+
+        "const a: any = 5",
+        "const fn = function(a: any = 5, b: any = true, c: any = 'foo') {}",
+
+        {
+            code:
+                "const fn = (a: number = 5, b: boolean = true, c: string = 'foo') => {}",
+            options: [{ ignoreParameters: true }]
+        },
+        {
+            code:
+                "function fn(a: number = 5, b: boolean = true, c: string = 'foo') {}",
+            options: [{ ignoreParameters: true }]
+        },
+        {
+            code:
+                "const fn = function(a: number = 5, b: boolean = true, c: string = 'foo') {}",
+            options: [{ ignoreParameters: true }]
+        },
+        {
+            code:
+                "class Foo { a: number = 5; b: boolean = true; c: string = 'foo'; }",
+            options: [{ ignoreProperties: true }]
+        }
+    ],
+
+    invalid: [
+        {
+            code: "const a: number = 5",
+            output: "const a = 5",
+            errors: [
+                {
+                    message:
+                        "Type number trivially inferred from a number literal, remove type annotation",
+                    line: 1,
+                    column: 7
+                }
+            ]
+        },
+        {
+            code: "const a: number = Infinity",
+            output: "const a = Infinity",
+            errors: [
+                {
+                    message:
+                        "Type number trivially inferred from a number literal, remove type annotation",
+                    line: 1,
+                    column: 7
+                }
+            ]
+        },
+        {
+            code: "const a: boolean = true",
+            output: "const a = true",
+            errors: [
+                {
+                    message:
+                        "Type boolean trivially inferred from a boolean literal, remove type annotation",
+                    line: 1,
+                    column: 7
+                }
+            ]
+        },
+        {
+            code: "const a: string = 'foo'",
+            output: "const a = 'foo'",
+            errors: [
+                {
+                    message:
+                        "Type string trivially inferred from a string literal, remove type annotation",
+                    line: 1,
+                    column: 7
+                }
+            ]
+        },
+        {
+            code:
+                "const fn = (a: number = 5, b: boolean = true, c: string = 'foo') => {}",
+            output: "const fn = (a = 5, b = true, c = 'foo') => {}",
+            errors: [
+                {
+                    message:
+                        "Type number trivially inferred from a number literal, remove type annotation",
+                    line: 1,
+                    column: 13
+                },
+                {
+                    message:
+                        "Type boolean trivially inferred from a boolean literal, remove type annotation",
+                    line: 1,
+                    column: 28
+                },
+                {
+                    message:
+                        "Type string trivially inferred from a string literal, remove type annotation",
+                    line: 1,
+                    column: 47
+                }
+            ]
+        },
+        {
+            code:
+                "class Foo { a: number = 5; b: boolean = true; c: string = 'foo'; }",
+            output: "class Foo { a = 5; b = true; c = 'foo'; }",
+            errors: [
+                {
+                    message:
+                        "Type number trivially inferred from a number literal, remove type annotation",
+                    line: 1,
+                    column: 13
+                },
+                {
+                    message:
+                        "Type boolean trivially inferred from a boolean literal, remove type annotation",
+                    line: 1,
+                    column: 28
+                },
+                {
+                    message:
+                        "Type string trivially inferred from a string literal, remove type annotation",
+                    line: 1,
+                    column: 47
+                }
+            ]
+        }
+    ]
+});
diff --git a/tests/lib/rules/no-var-requires.js b/tests/lib/rules/no-var-requires.js
new file mode 100644
index 0000000..4715cb9
--- /dev/null
+++ b/tests/lib/rules/no-var-requires.js
@@ -0,0 +1,56 @@
+/**
+ * @fileoverview Disallows the use of require statements except in import statements.
+ * @author Macklin Underdown
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const rule = require("../../../lib/rules/no-var-requires"),
+    RuleTester = require("eslint").RuleTester;
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+    parser: "typescript-eslint-parser"
+});
+
+ruleTester.run("no-var-requires", rule, {
+    valid: ["import foo = require('foo')", "require('foo')"],
+    invalid: [
+        {
+            code: "var foo = require('foo')",
+            errors: [
+                {
+                    message: "Require statement not part of import statement",
+                    line: 1,
+                    column: 11
+                }
+            ]
+        },
+        {
+            code: "const foo = require('foo')",
+            errors: [
+                {
+                    message: "Require statement not part of import statement",
+                    line: 1,
+                    column: 13
+                }
+            ]
+        },
+        {
+            code: "let foo = require('foo')",
+            errors: [
+                {
+                    message: "Require statement not part of import statement",
+                    line: 1,
+                    column: 11
+                }
+            ]
+        }
+    ]
+});
diff --git a/tests/lib/rules/type-annotation-spacing.js b/tests/lib/rules/type-annotation-spacing.js
index ab26609..2642425 100644
--- a/tests/lib/rules/type-annotation-spacing.js
+++ b/tests/lib/rules/type-annotation-spacing.js
@@ -3383,3 +3383,2527 @@ type Foo = {
         }
     ]
 });
+
+//------------------------------------------------------------------------------
+// Optional Annotation Tests
+//------------------------------------------------------------------------------
+
+ruleTester.run("type-annotation-spacing", rule, {
+    valid: [
+        `
+interface resolve {
+    resolver?: (() => PromiseLike<T>) | PromiseLike<T>;
+}
+        `,
+        "function foo(a?: string) {}",
+        `
+class Foo {
+    name?: string;
+}
+        `,
+        `
+class Foo {
+    constructor(message?: string);
+}
+        `,
+        `
+class Foo {
+    greet(name?: string): string { return name; }
+}
+        `,
+        `
+interface Foo {
+    name?: string;
+}
+        `,
+        `
+interface Foo {
+    greet(name?: string): string;
+}
+        `,
+        `
+interface Foo {
+    thing?: { [key in string]?: number };
+}
+        `,
+        `
+type Foo = {
+    name?: string;
+}
+        `,
+        `
+type Foo = {
+    greet(name?: string): string;
+}
+        `,
+        "type Foo = (name?: string) => string;",
+        `
+type Foo = {
+    greet?: (name?: string) => string;
+}
+        `,
+        {
+            code: "function foo(a?: string) {}",
+            options: [{ after: true }]
+        },
+        {
+            code: `
+class Foo {
+    name?: string;
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message?: string);
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: `
+class Foo {
+    greet(name?: string): string { return name; }
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: `
+interface Foo {
+    name?: string;
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name?: string): string;
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: `
+type Foo = {
+    name?: string;
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name?: string): string;
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: "type Foo = (name?: string) => string;",
+            options: [{ after: true }]
+        },
+        {
+            code: `
+type Foo = {
+    greet?: (name?: string) => string;
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: "function foo(a?: string) {}",
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+class Foo {
+    name?: string;
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message?: string);
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+class Foo {
+    greet(name?: string): string { return name; }
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+interface Foo {
+    name?: string;
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name?: string): string;
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+type Foo = {
+    name?: string;
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name?: string): string;
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: "type Foo = (name?: string)=> string;",
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+type Foo = {
+    greet?: (name?: string)=> string;
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: "function foo(a ?: string) {}",
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+class Foo {
+    name ?: string;
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message ?: string);
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+class Foo {
+    greet(name ?: string) : string { return name; }
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+interface Foo {
+    name ?: string;
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name ?: string) : string;
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    name ?: string;
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name ?: string) : string;
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: "type Foo = (name ?: string) => string;",
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    greet ?: (name : string) => string;
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: "function foo(a ?:string) {}",
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+class Foo {
+    name ?:string;
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message ?:string);
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+class Foo {
+    greet(name ?:string) :string { return name; }
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+interface Foo {
+    name ?:string;
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name ?:string) :string;
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    name ?:string;
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name ?:string) :string;
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: "type Foo = (name ?:string) =>string;",
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    greet :(name ?:string) =>string;
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: "function foo(a ?: string) {}",
+            options: [{ before: true }]
+        },
+        {
+            code: `
+class Foo {
+    name ?: string;
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message ?: string);
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: `
+class Foo {
+    greet(name ?: string) : string { return name; }
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: `
+interface Foo {
+    name ?: string;
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name ?: string) : string;
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    name ?: string;
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name ?: string) : string;
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: "type Foo = (name ?: string) => string;",
+            options: [{ before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    greet : (name ?: string) => string;
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: "function foo(a ?: string) {}",
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    name ?: string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message ?: string);
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    greet(name ?: string) : string { return name; }
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    name ?: string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name ?: string) : string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    name ?: string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name ?: string) : string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name ?: string)=>string;",
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet ?: (name ?: string)=>string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name ?: string) => string;",
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: {
+                        colon: {
+                            before: true,
+                            after: true
+                        },
+                        arrow: {
+                            before: true,
+                            after: true
+                        }
+                    }
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet ?: (name ?: string) => string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: {
+                        colon: {
+                            before: true,
+                            after: true
+                        },
+                        arrow: {
+                            before: true,
+                            after: true
+                        }
+                    }
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name ?: string) =>string;",
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: {
+                        colon: {
+                            before: true,
+                            after: true
+                        },
+                        arrow: {
+                            before: true
+                        }
+                    }
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet : (name ?: string) =>string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: {
+                        colon: {
+                            before: true,
+                            after: true
+                        },
+                        arrow: {
+                            before: true
+                        }
+                    }
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    thing?: { [key in string]?: number };
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: `
+interface Foo {
+    thing?: { [key in string]?: number };
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+interface Foo {
+    thing ?: { [key in string] ?: number };
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+interface Foo {
+    thing ?:{ [key in string] ?:number };
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+interface Foo {
+    thing ?: { [key in string] ?: number };
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    thing?: { [key in string]?: number };
+}
+            `
+        },
+        {
+            code: `
+type Foo = {
+    thing?: { [key in string]?: number };
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: `
+type Foo = {
+    thing?: { [key in string]?: number };
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+type Foo = {
+    thing ?: { [key in string] ?: number };
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    thing ?:{ [key in string] ?:number };
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+type Foo = {
+    thing ?: { [key in string] ?: number };
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: `
+class Foo {
+    greet: (name?: string) => void = {}
+}
+            `
+        },
+        {
+            code: `
+class Foo {
+    greet: (name?: string) => void = {}
+}
+            `,
+            options: [{ after: true }]
+        },
+        {
+            code: `
+class Foo {
+    greet: (name?: string)=> void = {}
+}
+            `,
+            options: [{ after: true, before: false }]
+        },
+        {
+            code: `
+class Foo {
+    greet : (name ?: string) => void = {}
+}
+            `,
+            options: [{ after: true, before: true }]
+        },
+        {
+            code: `
+class Foo {
+    greet :(name ?:string) =>void = {}
+}
+            `,
+            options: [{ after: false, before: true }]
+        },
+        {
+            code: `
+class Foo {
+    greet : (name ?: string) => void = {}
+}
+            `,
+            options: [{ before: true }]
+        },
+        {
+            code: `
+interface Foo { a?: string }
+type Bar = Record<keyof Foo, string>
+            `,
+            options: [
+                {
+                    after: true,
+                    before: false,
+                    overrides: {
+                        arrow: {
+                            after: true,
+                            before: true
+                        }
+                    }
+                }
+            ],
+            parser: "typescript-eslint-parser"
+        },
+        `
+interface resolve {
+    resolver?: (() => PromiseLike<T>) | PromiseLike<T>;
+}
+        `
+    ],
+    invalid: [
+        {
+            code: "function foo(a ?: string) {}",
+            output: "function foo(a?: string) {}",
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 1,
+                    column: 16
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    name ?: string;
+}
+            `,
+            output: `
+class Foo {
+    name?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message ?: string);
+}
+            `,
+            output: `
+class Foo {
+    constructor(message?: string);
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 25
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    greet(name ?: string) : string { return name; }
+}
+            `,
+            output: `
+class Foo {
+    greet(name?: string): string { return name; }
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Unexpected space before the ':'",
+                    line: 3,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    name ?: string;
+}
+            `,
+            output: `
+interface Foo {
+    name?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name ?: string) : string;
+}
+            `,
+            output: `
+interface Foo {
+    greet(name?: string): string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Unexpected space before the ':'",
+                    line: 3,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    name ?: string;
+}
+            `,
+            output: `
+type Foo = {
+    name?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name ?: string) : string;
+}
+            `,
+            output: `
+type Foo = {
+    greet(name?: string): string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Unexpected space before the ':'",
+                    line: 3,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name ?: string) => string;",
+            output: "type Foo = (name?: string) => string;",
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 1,
+                    column: 18
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name ?: string)=> string;",
+            output: "type Foo = (name?: string) => string;",
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 1,
+                    column: 18
+                },
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 1,
+                    column: 28
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet: (name ?: string) => string;
+}
+            `,
+            output: `
+type Foo = {
+    greet: (name?: string) => string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 18
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet: (name ?: string)=> string;
+}
+            `,
+            output: `
+type Foo = {
+    greet: (name?: string) => string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 18
+                },
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 3,
+                    column: 28
+                }
+            ]
+        },
+        {
+            code: "function foo(a ?: string) {}",
+            options: [{ after: true }],
+            output: "function foo(a?: string) {}",
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 1,
+                    column: 16
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    name ?: string;
+}
+            `,
+            options: [{ after: true }],
+            output: `
+class Foo {
+    name?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message ?: string);
+}
+            `,
+            options: [{ after: true }],
+            output: `
+class Foo {
+    constructor(message?: string);
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 25
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    greet(name ?: string) : string { return name; }
+}
+            `,
+            options: [{ after: true }],
+            output: `
+class Foo {
+    greet(name?: string): string { return name; }
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Unexpected space before the ':'",
+                    line: 3,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    name ?: string;
+}
+            `,
+            options: [{ after: true }],
+            output: `
+interface Foo {
+    name?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name ?: string) : string;
+}
+            `,
+            options: [{ after: true }],
+            output: `
+interface Foo {
+    greet(name?: string): string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Unexpected space before the ':'",
+                    line: 3,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    name ?: string;
+}
+            `,
+            options: [{ after: true }],
+            output: `
+type Foo = {
+    name?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name ?: string) : string;
+}
+            `,
+            options: [{ after: true }],
+            output: `
+type Foo = {
+    greet(name?: string): string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Unexpected space before the ':'",
+                    line: 3,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name ?: string) => string;",
+            options: [{ after: true }],
+            output: "type Foo = (name?: string) => string;",
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 1,
+                    column: 18
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name ?: string)=> string;",
+            options: [{ after: true }],
+            output: "type Foo = (name?: string) => string;",
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 1,
+                    column: 18
+                },
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 1,
+                    column: 28
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet: (name ?: string) => string;
+}
+            `,
+            options: [{ after: true }],
+            output: `
+type Foo = {
+    greet: (name?: string) => string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 18
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet: (name ?: string)=> string;
+}
+            `,
+            options: [{ after: true }],
+            output: `
+type Foo = {
+    greet: (name?: string) => string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 18
+                },
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 3,
+                    column: 28
+                }
+            ]
+        },
+        {
+            code: "function foo(a ?: string) {}",
+            options: [{ after: true, before: false }],
+            output: "function foo(a?: string) {}",
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 1,
+                    column: 16
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    name ?: string;
+}
+            `,
+            options: [{ after: true, before: false }],
+            output: `
+class Foo {
+    name?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message ?: string);
+}
+            `,
+            options: [{ after: true, before: false }],
+            output: `
+class Foo {
+    constructor(message?: string);
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 25
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    greet(name ?: string) : string { return name; }
+}
+            `,
+            options: [{ after: true, before: false }],
+            output: `
+class Foo {
+    greet(name?: string): string { return name; }
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Unexpected space before the ':'",
+                    line: 3,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    name ?: string;
+}
+            `,
+            options: [{ after: true, before: false }],
+            output: `
+interface Foo {
+    name?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name ?: string) : string;
+}
+            `,
+            options: [{ after: true, before: false }],
+            output: `
+interface Foo {
+    greet(name?: string): string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Unexpected space before the ':'",
+                    line: 3,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    name ?: string;
+}
+            `,
+            options: [{ after: true, before: false }],
+            output: `
+type Foo = {
+    name?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name ?: string) : string;
+}
+            `,
+            options: [{ after: true, before: false }],
+            output: `
+type Foo = {
+    greet(name?: string): string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Unexpected space before the ':'",
+                    line: 3,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name ?: string) => string;",
+            options: [{ after: true, before: false }],
+            output: "type Foo = (name?: string)=> string;",
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 1,
+                    column: 18
+                },
+                {
+                    message: "Unexpected space before the '=>'",
+                    line: 1,
+                    column: 29
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name ?: string)=> string;",
+            options: [{ after: true, before: false }],
+            output: "type Foo = (name?: string)=> string;",
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 1,
+                    column: 18
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet: (name ?: string) => string;
+}
+            `,
+            options: [{ after: true, before: false }],
+            output: `
+type Foo = {
+    greet: (name?: string)=> string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 18
+                },
+                {
+                    message: "Unexpected space before the '=>'",
+                    line: 3,
+                    column: 29
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet: (name ?: string)=> string;
+}
+            `,
+            options: [{ after: true, before: false }],
+            output: `
+type Foo = {
+    greet: (name?: string)=> string;
+}
+            `,
+            errors: [
+                {
+                    message: "Unexpected space before the '?:'",
+                    line: 3,
+                    column: 18
+                }
+            ]
+        },
+        {
+            code: "function foo(a?:string) {}",
+            options: [{ after: true, before: true }],
+            output: "function foo(a ?: string) {}",
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 1,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 1,
+                    column: 16
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    name?:string;
+}
+            `,
+            options: [{ after: true, before: true }],
+            output: `
+class Foo {
+    name ?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 9
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message?:string);
+}
+            `,
+            options: [{ after: true, before: true }],
+            output: `
+class Foo {
+    constructor(message ?: string);
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 25
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    greet(name?:string):string { return name; }
+}
+            `,
+            options: [{ after: true, before: true }],
+            output: `
+class Foo {
+    greet(name ?: string) : string { return name; }
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Expected a space after the ':'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space before the ':'",
+                    line: 3,
+                    column: 24
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    name?:string;
+}
+            `,
+            options: [{ after: true, before: true }],
+            output: `
+interface Foo {
+    name ?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 9
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name?:string):string;
+}
+            `,
+            options: [{ after: true, before: true }],
+            output: `
+interface Foo {
+    greet(name ?: string) : string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Expected a space after the ':'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space before the ':'",
+                    line: 3,
+                    column: 24
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    name?:string;
+}
+            `,
+            options: [{ after: true, before: true }],
+            output: `
+type Foo = {
+    name ?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 9
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name?:string):string;
+}
+            `,
+            options: [{ after: true, before: true }],
+            output: `
+type Foo = {
+    greet(name ?: string) : string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Expected a space after the ':'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space before the ':'",
+                    line: 3,
+                    column: 24
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name?: string)=> string;",
+            options: [{ after: true, before: true }],
+            output: "type Foo = (name ?: string) => string;",
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 1,
+                    column: 17
+                },
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 1,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name ?: string)=> string;",
+            options: [{ after: true, before: true }],
+            output: "type Foo = (name ?: string) => string;",
+            errors: [
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 1,
+                    column: 28
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet?: (name?: string)=> string;
+}
+            `,
+            options: [{ after: true, before: true }],
+            output: `
+type Foo = {
+    greet ?: (name ?: string) => string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 10
+                },
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 18
+                },
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 3,
+                    column: 28
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet ?: (name ?: string)=> string;
+}
+            `,
+            options: [{ after: true, before: true }],
+            output: `
+type Foo = {
+    greet ?: (name ?: string) => string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 3,
+                    column: 30
+                }
+            ]
+        },
+        {
+            code: "function foo(a?:string) {}",
+            options: [{ before: true }],
+            output: "function foo(a ?: string) {}",
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 1,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 1,
+                    column: 16
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    name?:string;
+}
+            `,
+            options: [{ before: true }],
+            output: `
+class Foo {
+    name ?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 9
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message?:string);
+}
+            `,
+            options: [{ before: true }],
+            output: `
+class Foo {
+    constructor(message ?: string);
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 25
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    greet(name?:string):string { return name; }
+}
+            `,
+            options: [{ before: true }],
+            output: `
+class Foo {
+    greet(name ?: string) : string { return name; }
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Expected a space after the ':'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space before the ':'",
+                    line: 3,
+                    column: 24
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    name?:string;
+}
+            `,
+            options: [{ before: true }],
+            output: `
+interface Foo {
+    name ?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 9
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name?:string):string;
+}
+            `,
+            options: [{ before: true }],
+            output: `
+interface Foo {
+    greet(name ?: string) : string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Expected a space after the ':'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space before the ':'",
+                    line: 3,
+                    column: 24
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    name?:string;
+}
+            `,
+            options: [{ before: true }],
+            output: `
+type Foo = {
+    name ?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 9
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name?:string):string;
+}
+            `,
+            options: [{ before: true }],
+            output: `
+type Foo = {
+    greet(name ?: string) : string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Expected a space after the ':'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space before the ':'",
+                    line: 3,
+                    column: 24
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name?: string)=> string;",
+            options: [{ before: true }],
+            output: "type Foo = (name ?: string) => string;",
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 1,
+                    column: 17
+                },
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 1,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name : string)=> string;",
+            options: [{ before: true }],
+            output: "type Foo = (name : string) => string;",
+            errors: [
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 1,
+                    column: 27
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet?: (name?: string)=> string;
+}
+            `,
+            options: [{ before: true }],
+            output: `
+type Foo = {
+    greet ?: (name ?: string) => string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 10
+                },
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 18
+                },
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 3,
+                    column: 28
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet ?: (name ?: string)=> string;
+}
+            `,
+            options: [{ before: true }],
+            output: `
+type Foo = {
+    greet ?: (name ?: string) => string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 3,
+                    column: 30
+                }
+            ]
+        },
+        {
+            code: "function foo(a?:string) {}",
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ],
+            output: "function foo(a ?: string) {}",
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 1,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 1,
+                    column: 16
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    name?:string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ],
+            output: `
+class Foo {
+    name ?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 9
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    constructor(message?:string);
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ],
+            output: `
+class Foo {
+    constructor(message ?: string);
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 25
+                }
+            ]
+        },
+        {
+            code: `
+class Foo {
+    greet(name?:string):string { return name; }
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ],
+            output: `
+class Foo {
+    greet(name ?: string) : string { return name; }
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Expected a space after the ':'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space before the ':'",
+                    line: 3,
+                    column: 24
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    name?:string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ],
+            output: `
+interface Foo {
+    name ?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 9
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    greet(name?:string):string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ],
+            output: `
+interface Foo {
+    greet(name ?: string) : string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Expected a space after the ':'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space before the ':'",
+                    line: 3,
+                    column: 24
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    name?:string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ],
+            output: `
+type Foo = {
+    name ?: string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 9
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 10
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet(name?:string):string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ],
+            output: `
+type Foo = {
+    greet(name ?: string) : string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 15
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 16
+                },
+                {
+                    message: "Expected a space after the ':'",
+                    line: 3,
+                    column: 24
+                },
+                {
+                    message: "Expected a space before the ':'",
+                    line: 3,
+                    column: 24
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name?:string)=>string;",
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ],
+            output: "type Foo = (name ?: string)=>string;",
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 1,
+                    column: 17
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 1,
+                    column: 18
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet ?: (name?:string)=>string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: { colon: { before: true, after: true } }
+                }
+            ],
+            output: `
+type Foo = {
+    greet ?: (name ?: string)=>string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 19
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 20
+                }
+            ]
+        },
+        {
+            code: "type Foo = (name?:string)=>string;",
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: {
+                        colon: {
+                            before: true,
+                            after: true
+                        },
+                        arrow: {
+                            before: true,
+                            after: true
+                        }
+                    }
+                }
+            ],
+            output: "type Foo = (name ?: string) => string;",
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 1,
+                    column: 17
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 1,
+                    column: 18
+                },
+                {
+                    message: "Expected a space after the '=>'",
+                    line: 1,
+                    column: 26
+                },
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 1,
+                    column: 26
+                }
+            ]
+        },
+        {
+            code: `
+type Foo = {
+    greet ?: (name?:string)=>string;
+}
+            `,
+            options: [
+                {
+                    before: false,
+                    after: false,
+                    overrides: {
+                        colon: {
+                            before: true,
+                            after: true
+                        },
+                        arrow: {
+                            before: true,
+                            after: true
+                        }
+                    }
+                }
+            ],
+            output: `
+type Foo = {
+    greet ?: (name ?: string) => string;
+}
+            `,
+            errors: [
+                {
+                    message: "Expected a space before the '?:'",
+                    line: 3,
+                    column: 19
+                },
+                {
+                    message: "Expected a space after the '?:'",
+                    line: 3,
+                    column: 20
+                },
+                {
+                    message: "Expected a space after the '=>'",
+                    line: 3,
+                    column: 28
+                },
+                {
+                    message: "Expected a space before the '=>'",
+                    line: 3,
+                    column: 28
+                }
+            ]
+        }
+    ]
+});