Repository: nzakas/eslint-plugin-typescript
Pull request #89 (2017-10-25 06:56:42)

Fix: Mark call expression type parameters as used
While type parameters on function declarations/expressions are marked as used, ones on call expressions are not, e.g.:

```ts
import { Foo } from 'foo'
function bar<T>() {}
bar<Foo>() // fails with no-unused-vars
```

timothykang: Updated to incorporate feedback, and added an invalid test case as well.

Patch:
diff --git a/lib/rules/no-triple-slash-reference.js b/lib/rules/no-triple-slash-reference.js
index c010343..49e8c32 100644
--- a/lib/rules/no-triple-slash-reference.js
+++ b/lib/rules/no-triple-slash-reference.js
@@ -35,9 +35,9 @@ module.exports = {
          * @private
          */
         function checkTripleSlashReference(program) {
-            const leading = sourceCode.getComments(program).leading;
+            const commentsBefore = sourceCode.getCommentsBefore(program);
 
-            leading.forEach(comment => {
+            commentsBefore.forEach(comment => {
                 if (comment.type !== "Line") {
                     return;
                 }
diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js
index 0f6aef8..f5b077f 100644
--- a/lib/rules/no-unused-vars.js
+++ b/lib/rules/no-unused-vars.js
@@ -69,15 +69,26 @@ module.exports = {
             const annotation = node.typeAnnotation || node;
 
             switch (annotation.type) {
+                case "Identifier": {
+                    markVariableAsUsed(context, annotation.name);
+                    break;
+                }
                 case "TSArrayType": {
                     markTypeAnnotationAsUsed(annotation.elementType);
                     break;
                 }
+                case "TSQualifiedName": {
+                    markTypeAnnotationAsUsed(annotation.left);
+                    markTypeAnnotationAsUsed(annotation.right);
+                    break;
+                }
                 case "TSTypeReference": {
                     if (annotation.typeName.type === "TSArrayType") {
                         markTypeAnnotationAsUsed(
                             annotation.typeName.elementType
                         );
+                    } else if (annotation.typeName.type === "TSQualifiedName") {
+                        markTypeAnnotationAsUsed(annotation.typeName);
                     } else {
                         markVariableAsUsed(context, annotation.typeName.name);
                         if (
@@ -92,6 +103,15 @@ module.exports = {
 
                     break;
                 }
+                case "TSTypeLiteral": {
+                    annotation.members.forEach(member => {
+                        if (member.typeAnnotation) {
+                            markTypeAnnotationAsUsed(member.typeAnnotation);
+                        }
+                    });
+
+                    break;
+                }
                 case "TSUnionType":
                 case "TSIntersectionType":
                     annotation.types.forEach(type => {
@@ -251,12 +271,26 @@ module.exports = {
             FunctionExpression: markFunctionReturnTypeAsUsed,
             ArrowFunctionExpression: markFunctionReturnTypeAsUsed,
 
+            CallExpression(node) {
+                if (node.typeParameters && node.typeParameters.params) {
+                    node.typeParameters.params.forEach(
+                        markTypeAnnotationAsUsed
+                    );
+                }
+            },
+
             Decorator: markDecoratorAsUsed,
             TSInterfaceHeritage: markExtendedInterfaceAsUsed,
 
             ClassDeclaration: markClassOptionsAsUsed,
             ClassExpression: markClassOptionsAsUsed,
 
+            ObjectPattern(node) {
+                if (node.typeAnnotation) {
+                    markTypeAnnotationAsUsed(node.typeAnnotation);
+                }
+            },
+
             MethodDefinition(node) {
                 if (node.decorators) {
                     node.decorators.forEach(markDecoratorAsUsed);
diff --git a/lib/rules/type-annotation-spacing.js b/lib/rules/type-annotation-spacing.js
index dd61c66..8a8aeb2 100644
--- a/lib/rules/type-annotation-spacing.js
+++ b/lib/rules/type-annotation-spacing.js
@@ -45,6 +45,7 @@ module.exports = {
     },
 
     create(context) {
+        const punctuators = [":", "=>"];
         const sourceCode = context.getSourceCode();
         const options = context.options[0] || {};
 
@@ -78,12 +79,16 @@ module.exports = {
             const nextToken = typeAnnotation;
             const punctuatorToken = sourceCode.getTokenBefore(nextToken);
             const previousToken = sourceCode.getTokenBefore(punctuatorToken);
+            const type = punctuatorToken.value;
+
+            if (punctuators.indexOf(type) === -1) {
+                return;
+            }
 
             const previousDelta =
                 punctuatorToken.range[0] - previousToken.range[1];
             const nextDelta = nextToken.range[0] - punctuatorToken.range[1];
 
-            const type = punctuatorToken.value;
             const before =
                 type === ":" ? colonOptions.before : arrowOptions.before;
             const after =
@@ -159,9 +164,7 @@ module.exports = {
                 }
             },
             TypeAnnotation(node) {
-                if (node.parent.type !== "TSAsExpression") {
-                    checkTypeAnnotationSpacing(node.typeAnnotation);
-                }
+                checkTypeAnnotationSpacing(node.typeAnnotation);
             },
             FunctionDeclaration: checkFunctionReturnTypeSpacing,
             FunctionExpression: checkFunctionReturnTypeSpacing,


Test patch:
diff --git a/tests/lib/rules/no-triple-slash-reference.js b/tests/lib/rules/no-triple-slash-reference.js
index 9656fc6..dd0a11f 100644
--- a/tests/lib/rules/no-triple-slash-reference.js
+++ b/tests/lib/rules/no-triple-slash-reference.js
@@ -39,6 +39,20 @@ ruleTester.run("no-triple-slash-reference", rule, {
                     column: 1
                 }
             ]
+        },
+        {
+            code: `
+/// <reference path="Animal" />
+let a
+            `,
+            parser: "typescript-eslint-parser",
+            errors: [
+                {
+                    message: "Do not use a triple slash reference",
+                    line: 2,
+                    column: 1
+                }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/no-unused-vars.js b/tests/lib/rules/no-unused-vars.js
index fbc5937..52c8687 100644
--- a/tests/lib/rules/no-unused-vars.js
+++ b/tests/lib/rules/no-unused-vars.js
@@ -184,6 +184,36 @@ ruleTester.run("no-unused-vars", ruleNoUnusedVars, {
             ].join("\n"),
             parser
         },
+        {
+            code: [
+                "import { Foo } from 'foo'",
+                "function bar<T>() {}",
+                "bar<Foo>()"
+            ].join("\n"),
+            parser
+        },
+        {
+            code: [
+                "import { Foo } from 'foo'",
+                "const bar = function <T>() {}",
+                "bar<Foo>()"
+            ].join("\n"),
+            parser
+        },
+        {
+            code: [
+                "import { Foo } from 'foo'",
+                "const bar = <T>() => {}",
+                "bar<Foo>()"
+            ].join("\n"),
+            parser
+        },
+        {
+            code: ["import { Foo } from 'foo'", "<Foo>(<T>() => {})()"].join(
+                "\n"
+            ),
+            parser
+        },
         {
             code: [
                 "import { Nullable } from 'nullable'",
@@ -373,6 +403,22 @@ ruleTester.run("no-unused-vars", ruleNoUnusedVars, {
             ].join("\n"),
             parser
         },
+        {
+            code: [
+                "import { Nullable } from 'nullable'",
+                "const foo = ({ nullable }: Nullable) => nullable",
+                "foo({ nullable: null })"
+            ].join("\n"),
+            parser
+        },
+        {
+            code: [
+                "import { ReproInterface } from 'ReproInterface'",
+                "const x = ({ a = null } : { a: ReproInterface }) => a",
+                "console.log(x)"
+            ].join("\n"),
+            parser
+        },
         {
             code: [
                 "import { Nullable } from 'nullable'",
@@ -418,6 +464,22 @@ ruleTester.run("no-unused-vars", ruleNoUnusedVars, {
                 "}"
             ].join("\n"),
             parser
+        },
+        {
+            code: [
+                "import Foo from 'foo'",
+                "const bar: Foo.Bar = null",
+                "console.log(bar)"
+            ].join("\n"),
+            parser
+        },
+        {
+            code: [
+                "import Foo from 'foo'",
+                "const baz: Foo.Bar.Baz = null",
+                "console.log(baz)"
+            ].join("\n"),
+            parser
         }
     ],
 
@@ -437,6 +499,21 @@ ruleTester.run("no-unused-vars", ruleNoUnusedVars, {
                 }
             ]
         },
+        {
+            code: [
+                "import { Foo, Bar } from 'foo';",
+                "function baz<Foo>() {}",
+                "baz<Bar>()"
+            ].join("\n"),
+            parser,
+            errors: [
+                {
+                    message: "'Foo' is defined but never used.",
+                    line: 1,
+                    column: 10
+                }
+            ]
+        },
         {
             code: [
                 "import { Nullable } from 'nullable';",
diff --git a/tests/lib/rules/type-annotation-spacing.js b/tests/lib/rules/type-annotation-spacing.js
index 2400ea1..bd6b5d5 100644
--- a/tests/lib/rules/type-annotation-spacing.js
+++ b/tests/lib/rules/type-annotation-spacing.js
@@ -19,6 +19,14 @@ const ruleTester = new RuleTester();
 
 ruleTester.run("type-annotation-spacing", rule, {
     valid: [
+        {
+            code: `
+interface resolve {
+    resolver: (() => PromiseLike<T>) | PromiseLike<T>;
+}
+            `,
+            parser: "typescript-eslint-parser"
+        },
         {
             code: "const foo = {} as Foo;",
             parser: "typescript-eslint-parser"
@@ -1175,6 +1183,18 @@ class Foo {
             `,
             options: [{ before: true }],
             parser: "typescript-eslint-parser"
+        },
+        {
+            code: "let resolver: (() => PromiseLike<T>) | PromiseLike<T>;",
+            parser: "typescript-eslint-parser"
+        },
+        {
+            code: `
+interface resolve {
+    resolver: (() => PromiseLike<T>) | PromiseLike<T>;
+}
+            `,
+            parser: "typescript-eslint-parser"
         }
     ],
     invalid: [