Skip to content
This repository was archived by the owner on Jan 19, 2019. It is now read-only.

Commit cd8a30e

Browse files
authored
Merge pull request #9 from nzakas/typescript-uses-vars
New: Mark interfaces and generators as used (refs #2)
2 parents dbe6e08 + 67ad1cb commit cd8a30e

File tree

4 files changed

+394
-1
lines changed

4 files changed

+394
-1
lines changed

docs/rules/no-unused-vars.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Prevent TypeScript-specific constructs from being erroneously flagged as unused (no-unused-vars)
2+
3+
This rule only has an effect when the `no-unused-vars` core rule is enabled.
4+
5+
It ensures that TypeScript-specific constructs, such as implemented interfaces, are not erroneously flagged as unused.
6+
7+
## Rule Details
8+
9+
The following patterns are considered warnings:
10+
11+
```ts
12+
interface Foo {}
13+
```
14+
15+
The following patterns are not warnings:
16+
17+
```js
18+
interface Foo {}
19+
20+
class Bar implements Foo {}
21+
```
22+
23+
## When Not To Use It
24+
25+
If you are not using `no-unused-vars` then you will not need this rule.

lib/rules/no-unused-vars.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* @fileoverview Prevent TypeScript-specific variables being falsely marked as unused
3+
* @author James Henry
4+
*/
5+
"use strict";
6+
7+
/**
8+
* Record that a particular variable has been used in code
9+
*
10+
* @param {Object} context The current rule context.
11+
* @param {String} name The name of the variable to mark as used.
12+
* @returns {Boolean} True if the variable was found and marked as used, false if not.
13+
*/
14+
function markVariableAsUsed(context, name) {
15+
var scope = context.getScope();
16+
var variables;
17+
var i;
18+
var len;
19+
var found = false;
20+
21+
// Special Node.js scope means we need to start one level deeper
22+
if (scope.type === "global") {
23+
while (scope.childScopes.length) {
24+
scope = scope.childScopes[0];
25+
}
26+
}
27+
28+
do {
29+
variables = scope.variables;
30+
for (i = 0, len = variables.length; i < len; i++) {
31+
if (variables[i].name === name) {
32+
variables[i].eslintUsed = true;
33+
found = true;
34+
}
35+
}
36+
scope = scope.upper;
37+
} while (scope);
38+
39+
return found;
40+
}
41+
42+
//------------------------------------------------------------------------------
43+
// Rule Definition
44+
//------------------------------------------------------------------------------
45+
46+
module.exports = {
47+
meta: {
48+
docs: {
49+
description: "Prevent TypeScript-specific variables being falsely marked as unused.",
50+
category: "TypeScript",
51+
recommended: true
52+
},
53+
schema: []
54+
},
55+
56+
create: function(context) {
57+
58+
//----------------------------------------------------------------------
59+
// Helpers
60+
//----------------------------------------------------------------------
61+
62+
/**
63+
* Checks if the given node has any decorators and marks them as used.
64+
* @param {ASTNode} node The relevant AST node.
65+
* @returns {void}
66+
* @private
67+
*/
68+
function markDecoratorsAsUsed(node) {
69+
if (!node.decorators || !node.decorators.length) {
70+
return;
71+
}
72+
node.decorators.forEach(function(decorator) {
73+
/**
74+
* Decorator
75+
*/
76+
if (decorator.name) {
77+
markVariableAsUsed(context, decorator.name);
78+
return;
79+
}
80+
/**
81+
* Decorator Factory
82+
*/
83+
if (decorator.callee && decorator.callee.name) {
84+
markVariableAsUsed(context, decorator.callee.name);
85+
return;
86+
}
87+
});
88+
}
89+
90+
/**
91+
* Checks if the given node has any implemented interfaces and marks them as used.
92+
* @param {ASTNode} node The relevant AST node.
93+
* @returns {void}
94+
* @private
95+
*/
96+
function markImplementedInterfacesAsUsed(node) {
97+
if (!node.implements || !node.implements.length) {
98+
return;
99+
}
100+
node.implements.forEach(function(implementedInterface) {
101+
if (!implementedInterface || !implementedInterface.id || !implementedInterface.id.name) {
102+
return;
103+
}
104+
markVariableAsUsed(context, implementedInterface.id.name);
105+
});
106+
}
107+
108+
//----------------------------------------------------------------------
109+
// Public
110+
//----------------------------------------------------------------------
111+
return {
112+
ClassProperty: markDecoratorsAsUsed,
113+
ClassDeclaration: function(node) {
114+
markDecoratorsAsUsed(node);
115+
markImplementedInterfacesAsUsed(node);
116+
},
117+
MethodDefinition: function(node) {
118+
/**
119+
* Decorators are only supported on class methods, so exit early
120+
* if the parent is not a ClassBody
121+
*/
122+
const anc = context.getAncestors();
123+
const tAnc = anc.length;
124+
if (!tAnc || !anc[tAnc - 1] || anc[tAnc - 1].type !== "ClassBody") {
125+
return;
126+
}
127+
/**
128+
* Mark any of the method's own decorators as used
129+
*/
130+
markDecoratorsAsUsed(node);
131+
/**
132+
* Mark any parameter decorators as used
133+
*/
134+
if (!node.value || !node.value.params || !node.value.params.length) {
135+
return;
136+
}
137+
node.value.params.forEach(markDecoratorsAsUsed);
138+
},
139+
};
140+
141+
}
142+
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"requireindex": "~1.1.0"
1919
},
2020
"devDependencies": {
21-
"eslint": "~3.0.0",
21+
"eslint": "~3.7.0",
2222
"eslint-config-eslint": "^3.0.0",
2323
"mocha": "^2.4.5",
2424
"typescript-eslint-parser": "^0.4.0"

0 commit comments

Comments
 (0)