Repository: nzakas/eslint-plugin-typescript
Pull request #113 (2018-03-09 15:43:05)

New: Add generic-type-naming (#111)
https://github.com/nzakas/eslint-plugin-typescript/issues/111


Patch:
diff --git a/README.md b/README.md
index ccc1723..64af18d 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,7 @@ Then configure the rules you want to use under the rules section.
 * [`typescript/class-name-casing`](./docs/rules/class-name-casing.md) — Require PascalCased class and interface names (`class-name` from TSLint)
 * [`typescript/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) — Require explicit return types on functions and class methods
 * [`typescript/explicit-member-accessibility`](./docs/rules/explicit-member-accessibility.md) — Require explicit accessibility modifiers on class properties and methods (`member-access` from TSLint)
+* [`typescript/generic-type-naming`](./docs/rules/generic-type-naming.md) — Enforces naming of generic type variables
 * [`typescript/interface-name-prefix`](./docs/rules/interface-name-prefix.md) — Require that interface names be prefixed with `I` (`interface-name` from TSLint)
 * [`typescript/member-delimiter-style`](./docs/rules/member-delimiter-style.md) — Require a specific member delimiter style for interfaces and type literals
 * [`typescript/member-naming`](./docs/rules/member-naming.md) — Enforces naming conventions for class members by visibility.
@@ -63,6 +64,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 +72,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/explicit-function-return-type.md b/docs/rules/explicit-function-return-type.md
index 459b184..a6f1ac1 100644
--- a/docs/rules/explicit-function-return-type.md
+++ b/docs/rules/explicit-function-return-type.md
@@ -57,6 +57,34 @@ class Test {
 }
 ```
 
+## Options
+
+The rule accepts an options object with the following properties:
+
+* `allowExpressions` if true, only functions which are part of a declaration will be checked
+
+By default, `allowExpressions: false` is used, meaning all declarations and
+expressions _must_ have a return type.
+
+### allowExpressions
+
+Examples of **incorrect** code for this rule with `{ allowExpressions: true }`:
+
+```ts
+function test() {
+}
+```
+
+Examples of **correct** code for this rule with `{ allowExpressions: true }`:
+
+```ts
+node.addEventListener('click', () => {});
+
+node.addEventListener('click', function() {});
+
+const foo = arr.map(i => i * i);
+```
+
 ## When Not To Use It
 
 If you don't wish to prevent calling code from using function return values in unexpected ways, then
diff --git a/docs/rules/generic-type-naming.md b/docs/rules/generic-type-naming.md
new file mode 100644
index 0000000..a13513d
--- /dev/null
+++ b/docs/rules/generic-type-naming.md
@@ -0,0 +1,42 @@
+# Enforces naming of generic type variables (generic-type-naming)
+
+It can be helpful to enforce a consistent naming style for generic type variables used within a type.
+For example, prefixing them with `T` and ensuring a somewhat descriptive name, or enforcing Hungarian notation.
+
+## Rule Details
+
+This rule allows you to enforce conventions over type variables. By default, it does nothing.
+
+## Options
+
+The rule takes a single string option, which is a regular expression that type variables should match.
+
+Examples of **correct** code with a configuration of `'^T[A-Z][a-zA-Z]+$'`:
+
+```typescript
+type ReadOnly<TType extends object> = {
+  readonly [TKey in keyof TType]: TType[TKey];
+}
+
+interface SimpleMap<TValue> {
+  [key: string]: TValue;
+}
+```
+
+Examples of **incorrect** code with a configuration of `'^T[A-Z][a-zA-Z]+$'`:
+
+```typescript
+type ReadOnly<T extends object> = {
+  readonly [Key in keyof T]: T[Key];
+}
+
+interface SimpleMap<T> {
+  [key: string]: T;
+}
+```
+
+## When Not To Use It
+If you do not want to enforce a naming convention for type variables.
+
+## Further Reading
+- [TypeScript Generics](https://www.typescriptlang.org/docs/handbook/generics.html)
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/adjacent-overload-signatures.js b/lib/rules/adjacent-overload-signatures.js
index 00feded..d40bed3 100644
--- a/lib/rules/adjacent-overload-signatures.js
+++ b/lib/rules/adjacent-overload-signatures.js
@@ -44,7 +44,9 @@ module.exports = {
                 }
                 case "DeclareFunction":
                 case "FunctionDeclaration":
-                case "TSNamespaceFunctionDeclaration": {
+                case "TSNamespaceFunctionDeclaration":
+                case "TSEmptyBodyFunctionDeclaration":
+                case "TSEmptyBodyDeclareFunction": {
                     return member.id.name;
                 }
                 case "TSMethodSignature": {
diff --git a/lib/rules/explicit-function-return-type.js b/lib/rules/explicit-function-return-type.js
index 7aea242..30ed3b4 100644
--- a/lib/rules/explicit-function-return-type.js
+++ b/lib/rules/explicit-function-return-type.js
@@ -20,10 +20,22 @@ module.exports = {
             url:
                 "https://github.com/nzakas/eslint-plugin-typescript/blob/master/docs/rules/explicit-function-return-type.md"
         },
-        schema: []
+        schema: [
+            {
+                type: "object",
+                properties: {
+                    allowExpressions: {
+                        type: "boolean"
+                    }
+                },
+                additionalProperties: false
+            }
+        ]
     },
 
     create(context) {
+        const options = context.options[0] || {};
+
         //----------------------------------------------------------------------
         // Helpers
         //----------------------------------------------------------------------
@@ -58,6 +70,14 @@ module.exports = {
          * @private
          */
         function checkFunctionReturnType(node) {
+            if (
+                options.allowExpressions &&
+                node.type !== "FunctionDeclaration" &&
+                node.parent.type !== "VariableDeclarator"
+            ) {
+                return;
+            }
+
             if (
                 !node.returnType &&
                 !isConstructor(node.parent) &&
diff --git a/lib/rules/generic-type-naming.js b/lib/rules/generic-type-naming.js
new file mode 100644
index 0000000..10e66e5
--- /dev/null
+++ b/lib/rules/generic-type-naming.js
@@ -0,0 +1,71 @@
+/**
+ * @fileoverview Enforces naming of generic type variables.
+ */
+"use strict";
+
+/**
+ *
+ * @param {any} context ESLint context
+ * @param {string} rule Option
+ * @returns {Function} Node's visitor function
+ */
+function createTypeParameterChecker(context, rule) {
+    const regex = new RegExp(rule);
+
+    return function checkTypeParameters(pnode) {
+        const params = pnode.typeParameters && pnode.typeParameters.params;
+
+        if (!Array.isArray(params) || params.length === 0) {
+            return;
+        }
+        params.forEach(node => {
+            const type = node.type;
+
+            if (type === "TSTypeParameter" || type === "TypeParameter") {
+                const name = node.name;
+
+                if (name && !regex.test(name)) {
+                    const data = { name, rule };
+
+                    context.report({
+                        node,
+                        message:
+                            "Type parameter {{name}} does not match rule {{rule}}",
+                        data
+                    });
+                }
+            }
+        });
+    };
+}
+
+module.exports = {
+    meta: {
+        docs: {
+            description: "Enforces naming of generic type variables",
+            category: "TypeScript",
+            url:
+                "https://github.com/nzakas/eslint-plugin-typescript/blob/master/docs/rules/generic-type-naming.md"
+        }
+    },
+
+    create(context) {
+        const rule = context.options[0];
+
+        if (!rule) {
+            return {};
+        }
+
+        const checkTypeParameters = createTypeParameterChecker(context, rule);
+
+        return {
+            VariableDeclarator: checkTypeParameters,
+            ClassDeclaration: checkTypeParameters,
+            InterfaceDeclaration: checkTypeParameters,
+            TSInterfaceDeclaration: checkTypeParameters,
+            FunctionDeclaration: checkTypeParameters,
+            TSCallSignature: checkTypeParameters,
+            CallSignature: checkTypeParameters
+        };
+    }
+};
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..d9ba96c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-typescript",
-  "version": "0.10.0",
+  "version": "0.12.0",
   "description": "TypeScript plugin for ESLint",
   "keywords": [
     "eslint",
@@ -27,14 +27,14 @@
     "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",
-    "typescript": "~2.6.1",
-    "typescript-eslint-parser": "^10.0.0"
+    "prettier": "^1.11.1",
+    "typescript": "~2.8.1",
+    "typescript-eslint-parser": "^15.0.0"
   },
   "lint-staged": {
     "*.js": [


Test patch:
diff --git a/tests/lib/rules/explicit-function-return-type.js b/tests/lib/rules/explicit-function-return-type.js
index f6287a4..64892ff 100644
--- a/tests/lib/rules/explicit-function-return-type.js
+++ b/tests/lib/rules/explicit-function-return-type.js
@@ -65,6 +65,46 @@ function test() {
     return;
 }
             `
+        },
+        {
+            code: `fn(() => {});`,
+            options: [
+                {
+                    allowExpressions: true
+                }
+            ]
+        },
+        {
+            code: `fn(function() {});`,
+            options: [
+                {
+                    allowExpressions: true
+                }
+            ]
+        },
+        {
+            code: `[function() {}, () => {}]`,
+            options: [
+                {
+                    allowExpressions: true
+                }
+            ]
+        },
+        {
+            code: `(function() {});`,
+            options: [
+                {
+                    allowExpressions: true
+                }
+            ]
+        },
+        {
+            code: `(() => {})();`,
+            options: [
+                {
+                    allowExpressions: true
+                }
+            ]
         }
     ],
     invalid: [
@@ -137,6 +177,30 @@ class Test {
                     column: 9
                 }
             ]
+        },
+        {
+            filename: "test.ts",
+            code: `const foo = () => {};`,
+            options: [{ allowExpressions: true }],
+            errors: [
+                {
+                    message: "Missing return type on function.",
+                    line: 1,
+                    column: 13
+                }
+            ]
+        },
+        {
+            filename: "test.ts",
+            code: `const foo = function() {};`,
+            options: [{ allowExpressions: true }],
+            errors: [
+                {
+                    message: "Missing return type on function.",
+                    line: 1,
+                    column: 13
+                }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/generic-type-naming.js b/tests/lib/rules/generic-type-naming.js
new file mode 100644
index 0000000..7f1ed3a
--- /dev/null
+++ b/tests/lib/rules/generic-type-naming.js
@@ -0,0 +1,74 @@
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const rule = require("../../../lib/rules/generic-type-naming"),
+    RuleTester = require("eslint").RuleTester;
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+    parser: "typescript-eslint-parser"
+});
+
+const messagePattern = "Type parameter {{name}} does not match rule {{rule}}";
+
+/**
+ * Creates error object
+ * @param {Object} data Data
+ * @returns {Object} Object with message property
+ */
+function error(data) {
+    let message = messagePattern;
+
+    Object.keys(data).forEach(key => {
+        message = message.replace(new RegExp(`{{${key}}}`, "g"), data[key]);
+    });
+    return { message };
+}
+
+ruleTester.run("generic-type-naming", rule, {
+    valid: [
+        { code: "class<T,U,V> { }", options: [] },
+        { code: "type ReadOnly<T extends object> = {}", options: [] },
+        { code: "interface SimpleMap<V> { }", options: [] },
+        { code: "function get<T>() {}", options: [] },
+        { code: "interface GenericIdentityFn { <T>(arg: T): T }", options: [] },
+        { code: "class<x> { }", options: ["^x+$"] },
+        {
+            code: "class CounterContainer extends Container<Counter> { }",
+            options: ["^T$"]
+        }
+    ],
+    invalid: [
+        {
+            code: "class<x> { }",
+            options: ["^[A-Z]+$"],
+            errors: [error({ name: "x", rule: "^[A-Z]+$" })]
+        },
+        {
+            code: "interface SimpleMap<x> { }",
+            options: ["^[A-Z]+$"],
+            errors: [error({ name: "x", rule: "^[A-Z]+$" })]
+        },
+        {
+            code: "type R<x> = {}",
+            options: ["^[A-Z]+$"],
+            errors: [error({ name: "x", rule: "^[A-Z]+$" })]
+        },
+        {
+            code: "function get<x>() {}",
+            options: ["^[A-Z]+$"],
+            errors: [error({ name: "x", rule: "^[A-Z]+$" })]
+        },
+        {
+            code: "interface GenericIdentityFn { <x>(arg: x): x }",
+            options: ["^[A-Z]+$"],
+            errors: [error({ name: "x", rule: "^[A-Z]+$" })]
+        }
+    ]
+});
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
+                }
+            ]
+        }
+    ]
+});