Repository: nzakas/eslint-plugin-typescript
Pull request #39 (2017-08-11 03:50:48)

New: Add no-empty-interface
This includes

New rule no-empty-interface
Corresponding test
Corresponding documentation
Update to README.md to include the new rule

Related to #5 (TSLint compatibility)

weirdpattern: I will rebase this as soon as I get home...
corbinu: LGTM
JamesHenry: Thanks! The older README.md cleanup PR that went in means this just needs to be rebased before going in
weirdpattern: @JamesHenry Done... either @j-f1 or myself will have to rebase one or more PRs once you have merged one of these, just let us know and we will take care of it 😄 

Patch:
diff --git a/README.md b/README.md
index 0faab53..0262e78 100644
--- a/README.md
+++ b/README.md
@@ -62,3 +62,4 @@ Then configure the rules you want to use under the rules section.
 * [`typescript/member-ordering`](./docs/rules/member-ordering.md) — enforces a standard member declaration order. (`member-ordering` from TSLint)
 * [`typescript/no-unused-vars`](./docs/rules/no-unused-vars.md) — prevents TypeScript-specific constructs from being erroneously flagged as unused
 * [`typescript/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) — enforces member overloads to be consecutive.
+* [`typescript/no-empty-interface`](./docs/rules/no-empty-interface.md) - disallows the declaration of empty interfaces. (`no-empty-interface` from TSLint)
diff --git a/docs/rules/no-empty-interface.md b/docs/rules/no-empty-interface.md
new file mode 100644
index 0000000..fa41331
--- /dev/null
+++ b/docs/rules/no-empty-interface.md
@@ -0,0 +1,45 @@
+# Disallows the declaration of empty interfaces.
+
+An empty interface is equivalent to its supertype. If the interface does not implement a supertype, then 
+the interface is equivalent to an empty object (`{}`). In both cases it can be omitted.
+
+## Rule Details
+
+This rule aims to ensure that only meaningful interfaces are declared in the code.
+
+The following patterns are considered warnings:
+```ts
+// an empty interface
+interface Foo { }
+
+// an interface with only one supertype (Bar === Foo)
+interface Bar extends Foo { }
+
+// an interface with an empty list of supertypes
+interface Baz extends { }
+```
+
+The following patterns are not warnings:
+```ts
+// an interface with any number of members
+interface Foo {
+    name: string;
+}
+
+// same as above
+interface Bar {
+    age: number;
+}
+
+// an interface with more than one supertype
+// in this case the interface can be used as a replacement of a union type.
+interface Baz extends Foo, Bar { }
+```
+
+## When Not To Use It
+
+If you don't care about having empty/meaningless interfaces, then you will not need this rule.
+
+## Compatibility
+
+* TSLint: [no-empty-interface](https://palantir.github.io/tslint/rules/no-empty-interface/)
\ No newline at end of file
diff --git a/lib/rules/no-empty-interface.js b/lib/rules/no-empty-interface.js
new file mode 100644
index 0000000..0fe2735
--- /dev/null
+++ b/lib/rules/no-empty-interface.js
@@ -0,0 +1,40 @@
+/**
+ * @fileoverview Disallows the declaration of empty interfaces.
+ * @author Patricio Trevino
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        docs: {
+            description: "Disallows the declaration of empty interfaces.",
+            category: "TypeScript"
+        },
+        schema: []
+    },
+
+    //----------------------------------------------------------------------
+    // Public
+    //----------------------------------------------------------------------
+
+    create(context) {
+        return {
+            TSInterfaceDeclaration(node) {
+                const heritage = node.heritage.length;
+
+                if (node.body.body.length === 0 && heritage < 2) {
+                    context.report({
+                        node: node.id,
+                        message: heritage === 0
+                            ? "An empty interface is equivalent to `{}`"
+                            : "An interface declaring no members is equivalent to its supertype."
+                    });
+                }
+            }
+        };
+    }
+};


Test patch:
diff --git a/tests/lib/rules/no-empty-interface.js b/tests/lib/rules/no-empty-interface.js
new file mode 100644
index 0000000..96481fa
--- /dev/null
+++ b/tests/lib/rules/no-empty-interface.js
@@ -0,0 +1,87 @@
+/**
+ * @fileoverview Disallows the declaration of empty interfaces.
+ * @author Patricio Trevino
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const rule = require("../../../lib/rules/no-empty-interface"),
+    RuleTester = require("eslint").RuleTester;
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester();
+
+ruleTester.run("no-empty-interface", rule, {
+    valid: [
+        {
+            code: `
+interface Foo {
+    name: string;
+}
+            `,
+            parser: "typescript-eslint-parser"
+        },
+        {
+            code: `
+interface Foo {
+    name: string;
+}
+
+interface Bar {
+    age: number;
+}
+
+// valid because extending multiple interfaces can be used instead of a union type
+interface Baz extends Foo, Bar {}
+            `,
+            parser: "typescript-eslint-parser"
+        }
+    ],
+    invalid: [
+        {
+            code: "interface Foo {}",
+            parser: "typescript-eslint-parser",
+            errors: [
+                {
+                    message: "An empty interface is equivalent to `{}`",
+                    line: 1,
+                    column: 11
+                }
+            ]
+        },
+        {
+            code: "interface Foo extends {}",
+            parser: "typescript-eslint-parser",
+            errors: [
+                {
+                    message: "An empty interface is equivalent to `{}`",
+                    line: 1,
+                    column: 11
+                }
+            ]
+        },
+        {
+            code: `
+interface Foo {
+    name: string;    
+}
+
+interface Bar extends Foo {}
+            `,
+            parser: "typescript-eslint-parser",
+            errors: [
+                {
+                    message: "An interface declaring no members is equivalent to its supertype.",
+                    line: 6,
+                    column: 11
+                }
+            ]
+        }
+    ]
+});