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

Commit 0818053

Browse files
committed
Add ban-types rule
1 parent 7cf0b22 commit 0818053

File tree

4 files changed

+365
-0
lines changed

4 files changed

+365
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ This guarantees 100% compatibility between the plugin and the parser.
4949
<!-- Please run `npm run docs` to update this section -->
5050
<!-- begin rule list -->
5151
* [`typescript/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) — Require that member overloads be consecutive
52+
* [`typescript/ban-types`](./docs/rules/ban-types.md) — Enforces that types will not to be used (`ban-types` from TSLint)
5253
* [`typescript/camelcase`](./docs/rules/camelcase.md) — Enforce camelCase naming convention
5354
* [`typescript/class-name-casing`](./docs/rules/class-name-casing.md) — Require PascalCased class and interface names (`class-name` from TSLint)
5455
* [`typescript/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) — Require explicit return types on functions and class methods

docs/rules/ban-types.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Enforces that types will not to be used (ban-types)
2+
3+
Bans specific types from being used. Does not ban the corresponding runtime objects from being used.
4+
5+
## Rule Details
6+
7+
Examples of **incorrect** code for this rule `"String": "Use string instead"`
8+
9+
```ts
10+
class Foo<F = String> extends Bar<String> implements Baz<String> {
11+
constructor (foo: String) {
12+
}
13+
14+
exit () : Array<String> {
15+
const foo: String = 1 as String
16+
}
17+
}
18+
```
19+
20+
Examples of **correct** code for this rule `"String": "Use string instead"`
21+
22+
```ts
23+
class Foo<F = string> extends Bar<string> implements Baz<string> {
24+
constructor (foo: string) {
25+
}
26+
27+
exit () : Array<string> {
28+
const foo: string = 1 as string
29+
}
30+
}
31+
```
32+
33+
## Options
34+
```CJSON
35+
{
36+
"typescript/ban-types": ["error", {
37+
"types": {
38+
// report usages of the type using the default error message
39+
"Foo": null,
40+
41+
// add a custom message to help explain why not to use it
42+
"Bar": "Don't use bar!",
43+
44+
// add a custom message, AND tell the plugin how to fix it
45+
"String": {
46+
"message": "Use string instead",
47+
"fixWith": "string"
48+
}
49+
}
50+
}
51+
}
52+
```
53+
54+
### Example
55+
```json
56+
{
57+
"typescript/ban-types": ["error", {
58+
"types": {
59+
"Array": null,
60+
"Object": "Use {} instead",
61+
"String": {
62+
"message": "Use string instead",
63+
"fixWith": "string"
64+
}
65+
}
66+
}]
67+
}
68+
```
69+
70+
71+
## Compatibility
72+
73+
* TSLint: [ban-types](https://palantir.github.io/tslint/rules/ban-types/)

lib/rules/ban-types.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* @fileoverview Enforces that types will not to be used
3+
* @author Armano <https://github.com/armano2>
4+
*/
5+
"use strict";
6+
7+
const util = require("../util");
8+
9+
//------------------------------------------------------------------------------
10+
// Rule Definition
11+
//------------------------------------------------------------------------------
12+
13+
module.exports = {
14+
meta: {
15+
docs: {
16+
description: "Enforces that types will not to be used",
17+
extraDescription: [util.tslintRule("ban-types")],
18+
category: "TypeScript",
19+
url:
20+
"https://github.com/nzakas/eslint-plugin-typescript/blob/master/docs/rules/ban-types.md",
21+
},
22+
fixable: "code",
23+
messages: {
24+
bannedTypeMessage:
25+
"Don't use '{{name}}' as a type.{{customMessage}}",
26+
},
27+
schema: [
28+
{
29+
type: "object",
30+
properties: {
31+
types: {
32+
type: "object",
33+
additionalProperties: {
34+
oneOf: [
35+
{ type: "null" },
36+
{ type: "string" },
37+
{
38+
type: "object",
39+
properties: {
40+
message: { type: "string" },
41+
fixWith: { type: "string" },
42+
},
43+
additionalProperties: false,
44+
},
45+
],
46+
},
47+
},
48+
},
49+
additionalProperties: false,
50+
},
51+
],
52+
},
53+
54+
create(context) {
55+
const banedTypes = (context.options[0] || {}).types || {};
56+
57+
//----------------------------------------------------------------------
58+
// Public
59+
//----------------------------------------------------------------------
60+
61+
return {
62+
"TSTypeReference Identifier"(node) {
63+
if (node.parent && node.parent.type !== "TSQualifiedName") {
64+
if (node.name in banedTypes) {
65+
let customMessage = "";
66+
const bannedCfgValue = banedTypes[node.name];
67+
let fixWith = null;
68+
69+
if (typeof bannedCfgValue === "string") {
70+
customMessage += ` ${bannedCfgValue}`;
71+
} else if (bannedCfgValue !== null) {
72+
if (bannedCfgValue.message) {
73+
customMessage += ` ${bannedCfgValue.message}`;
74+
}
75+
if (bannedCfgValue.fixWith) {
76+
fixWith = bannedCfgValue.fixWith;
77+
}
78+
}
79+
80+
context.report({
81+
node,
82+
messageId: "bannedTypeMessage",
83+
data: {
84+
name: node.name,
85+
customMessage,
86+
},
87+
fix:
88+
fixWith !== null &&
89+
(fixer => fixer.replaceText(node, fixWith)),
90+
});
91+
}
92+
}
93+
},
94+
};
95+
},
96+
};

tests/lib/rules/ban-types.js

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/**
2+
* @fileoverview Enforces that types will not to be used
3+
* @author Armano <https://github.com/armano2>
4+
*/
5+
"use strict";
6+
7+
//------------------------------------------------------------------------------
8+
// Requirements
9+
//------------------------------------------------------------------------------
10+
11+
const rule = require("../../../lib/rules/ban-types"),
12+
RuleTester = require("eslint").RuleTester;
13+
14+
//------------------------------------------------------------------------------
15+
// Tests
16+
//------------------------------------------------------------------------------
17+
18+
const ruleTester = new RuleTester({
19+
parser: "typescript-eslint-parser",
20+
});
21+
22+
const options = [
23+
{
24+
types: {
25+
String: {
26+
message: "Use string instead.",
27+
fixWith: "string",
28+
},
29+
Object: "Use '{}' instead.",
30+
Array: null,
31+
F: null,
32+
},
33+
},
34+
];
35+
36+
ruleTester.run("ban-types", rule, {
37+
valid: [
38+
"let f = Object();", // Should not fail if there is no options set
39+
{
40+
code: "let f = Object();",
41+
options,
42+
},
43+
{
44+
code: "let g = Object.create(null);",
45+
options,
46+
},
47+
{
48+
code: "let h = String(false);",
49+
options,
50+
},
51+
{
52+
code: "let e: foo.String;",
53+
options,
54+
},
55+
],
56+
invalid: [
57+
{
58+
code: "let a: Object;",
59+
errors: [
60+
{
61+
message: "Don't use 'Object' as a type. Use '{}' instead.",
62+
line: 1,
63+
column: 8,
64+
},
65+
],
66+
options,
67+
},
68+
{
69+
code: "let b: {c: String};",
70+
output: "let b: {c: string};",
71+
errors: [
72+
{
73+
message:
74+
"Don't use 'String' as a type. Use string instead.",
75+
line: 1,
76+
column: 12,
77+
},
78+
],
79+
options,
80+
},
81+
{
82+
code: "function foo(a: String) {}",
83+
output: "function foo(a: string) {}",
84+
errors: [
85+
{
86+
message:
87+
"Don't use 'String' as a type. Use string instead.",
88+
line: 1,
89+
column: 17,
90+
},
91+
],
92+
options,
93+
},
94+
{
95+
code: "'a' as String;",
96+
output: "'a' as string;",
97+
errors: [
98+
{
99+
message:
100+
"Don't use 'String' as a type. Use string instead.",
101+
line: 1,
102+
column: 8,
103+
},
104+
],
105+
options,
106+
},
107+
{
108+
code: "let c: F;",
109+
errors: [
110+
{
111+
message: "Don't use 'F' as a type.",
112+
line: 1,
113+
column: 8,
114+
},
115+
],
116+
options,
117+
},
118+
{
119+
code: `
120+
class Foo<F = String> extends Bar<String> implements Baz<Object> {
121+
constructor (foo: String | Object) {
122+
}
123+
124+
exit () : Array<String> {
125+
const foo: String = 1 as String
126+
}
127+
}
128+
`,
129+
output: `
130+
class Foo<F = string> extends Bar<string> implements Baz<Object> {
131+
constructor (foo: string | Object) {
132+
}
133+
134+
exit () : Array<string> {
135+
const foo: string = 1 as string
136+
}
137+
}
138+
`,
139+
errors: [
140+
{
141+
message:
142+
"Don't use 'String' as a type. Use string instead.",
143+
line: 2,
144+
column: 27,
145+
},
146+
{
147+
message:
148+
"Don't use 'String' as a type. Use string instead.",
149+
line: 2,
150+
column: 47,
151+
},
152+
{
153+
message: "Don't use 'Object' as a type. Use '{}' instead.",
154+
line: 2,
155+
column: 70,
156+
},
157+
{
158+
message:
159+
"Don't use 'String' as a type. Use string instead.",
160+
line: 3,
161+
column: 35,
162+
},
163+
{
164+
message: "Don't use 'Object' as a type. Use '{}' instead.",
165+
line: 3,
166+
column: 44,
167+
},
168+
{
169+
message: "Don't use 'Array' as a type.",
170+
line: 6,
171+
column: 27,
172+
},
173+
{
174+
message:
175+
"Don't use 'String' as a type. Use string instead.",
176+
line: 6,
177+
column: 33,
178+
},
179+
{
180+
message:
181+
"Don't use 'String' as a type. Use string instead.",
182+
line: 7,
183+
column: 32,
184+
},
185+
{
186+
message:
187+
"Don't use 'String' as a type. Use string instead.",
188+
line: 7,
189+
column: 46,
190+
},
191+
],
192+
options,
193+
},
194+
],
195+
});

0 commit comments

Comments
 (0)