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

Commit

Permalink
Add ban-types rule
Browse files Browse the repository at this point in the history
  • Loading branch information
armano2 committed Dec 4, 2018
1 parent 3799a43 commit 2fdfc55
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ This guarantees 100% compatibility between the plugin and the parser.
<!-- Please run `npm run docs` to update this section -->
<!-- begin rule list -->
* [`typescript/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) — Require that member overloads be consecutive
* [`typescript/ban-types`](./docs/rules/ban-types.md) — Enforces that types will not to be used (`ban-types` from TSLint)
* [`typescript/camelcase`](./docs/rules/camelcase.md) — Enforce camelCase naming convention
* [`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
Expand Down
73 changes: 73 additions & 0 deletions docs/rules/ban-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Enforces that types will not to be used (ban-types)

Bans specific types from being used. Does not ban the corresponding runtime objects from being used.

## Rule Details

Examples of **incorrect** code for this rule `"String": "Use string instead"`

```ts
class Foo<F = String> extends Bar<String> implements Baz<String> {
constructor (foo: String) {
}

exit () : Array<String> {
const foo: String = 1 as String
}
}
```

Examples of **correct** code for this rule `"String": "Use string instead"`

```ts
class Foo<F = string> extends Bar<string> implements Baz<string> {
constructor (foo: string) {
}

exit () : Array<string> {
const foo: string = 1 as string
}
}
```

## Options
```CJSON
{
"typescript/ban-types": ["error", {
"types": {
// report usages of the type using the default error message
"Foo": null,
// add a custom message to help explain why not to use it
"Bar": "Don't use bar!",
// add a custom message, AND tell the plugin how to fix it
"String": {
"message": "Use string instead",
"fixWith": "string"
}
}
}
}
```

### Example
```json
{
"typescript/ban-types": ["error", {
"types": {
"Array": null,
"Object": "Use {} instead",
"String": {
"message": "Use string instead",
"fixWith": "string"
}
}
}]
}
```


## Compatibility

* TSLint: [ban-types](https://palantir.github.io/tslint/rules/ban-types/)
96 changes: 96 additions & 0 deletions lib/rules/ban-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* @fileoverview Enforces that types will not to be used
* @author Armano <https://github.com/armano2>
*/
"use strict";

const util = require("../util");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: "Enforces that types will not to be used",
extraDescription: [util.tslintRule("ban-types")],
category: "TypeScript",
url:
"https://github.com/nzakas/eslint-plugin-typescript/blob/master/docs/rules/ban-types.md",
},
fixable: "code",
messages: {
bannedTypeMessage:
"Don't use '{{name}}' as a type.{{customMessage}}",
},
schema: [
{
type: "object",
properties: {
types: {
type: "object",
additionalProperties: {
oneOf: [
{ type: "null" },
{ type: "string" },
{
type: "object",
properties: {
message: { type: "string" },
fixWith: { type: "string" },
},
additionalProperties: false,
},
],
},
},
},
additionalProperties: false,
},
],
},

create(context) {
const banedTypes = (context.options[0] || {}).types || {};

//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------

return {
"TSTypeReference Identifier"(node) {
if (node.parent && node.parent.type !== "TSQualifiedName") {
if (node.name in banedTypes) {
let customMessage = "";
const bannedCfgValue = banedTypes[node.name];
let fixWith = null;

if (typeof bannedCfgValue === "string") {
customMessage += ` ${bannedCfgValue}`;
} else if (bannedCfgValue !== null) {
if (bannedCfgValue.message) {
customMessage += ` ${bannedCfgValue.message}`;
}
if (bannedCfgValue.fixWith) {
fixWith = bannedCfgValue.fixWith;
}
}

context.report({
node,
messageId: "bannedTypeMessage",
data: {
name: node.name,
customMessage,
},
fix:
fixWith !== null &&
(fixer => fixer.replaceText(node, fixWith)),
});
}
}
},
};
},
};
195 changes: 195 additions & 0 deletions tests/lib/rules/ban-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/**
* @fileoverview Enforces that types will not to be used
* @author Armano <https://github.com/armano2>
*/
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const rule = require("../../../lib/rules/ban-types"),
RuleTester = require("eslint").RuleTester;

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester({
parser: "typescript-eslint-parser",
});

const options = [
{
types: {
String: {
message: "Use string instead.",
fixWith: "string",
},
Object: "Use '{}' instead.",
Array: null,
F: null,
},
},
];

ruleTester.run("ban-types", rule, {
valid: [
"let f = Object();", // Should not fail if there is no options set
{
code: "let f = Object();",
options,
},
{
code: "let g = Object.create(null);",
options,
},
{
code: "let h = String(false);",
options,
},
{
code: "let e: foo.String;",
options,
},
],
invalid: [
{
code: "let a: Object;",
errors: [
{
message: "Don't use 'Object' as a type. Use '{}' instead.",
line: 1,
column: 8,
},
],
options,
},
{
code: "let b: {c: String};",
output: "let b: {c: string};",
errors: [
{
message:
"Don't use 'String' as a type. Use string instead.",
line: 1,
column: 12,
},
],
options,
},
{
code: "function foo(a: String) {}",
output: "function foo(a: string) {}",
errors: [
{
message:
"Don't use 'String' as a type. Use string instead.",
line: 1,
column: 17,
},
],
options,
},
{
code: "'a' as String;",
output: "'a' as string;",
errors: [
{
message:
"Don't use 'String' as a type. Use string instead.",
line: 1,
column: 8,
},
],
options,
},
{
code: "let c: F;",
errors: [
{
message: "Don't use 'F' as a type.",
line: 1,
column: 8,
},
],
options,
},
{
code: `
class Foo<F = String> extends Bar<String> implements Baz<Object> {
constructor (foo: String | Object) {
}
exit () : Array<String> {
const foo: String = 1 as String
}
}
`,
output: `
class Foo<F = string> extends Bar<string> implements Baz<Object> {
constructor (foo: string | Object) {
}
exit () : Array<string> {
const foo: string = 1 as string
}
}
`,
errors: [
{
message:
"Don't use 'String' as a type. Use string instead.",
line: 2,
column: 27,
},
{
message:
"Don't use 'String' as a type. Use string instead.",
line: 2,
column: 47,
},
{
message: "Don't use 'Object' as a type. Use '{}' instead.",
line: 2,
column: 70,
},
{
message:
"Don't use 'String' as a type. Use string instead.",
line: 3,
column: 35,
},
{
message: "Don't use 'Object' as a type. Use '{}' instead.",
line: 3,
column: 44,
},
{
message: "Don't use 'Array' as a type.",
line: 6,
column: 27,
},
{
message:
"Don't use 'String' as a type. Use string instead.",
line: 6,
column: 33,
},
{
message:
"Don't use 'String' as a type. Use string instead.",
line: 7,
column: 32,
},
{
message:
"Don't use 'String' as a type. Use string instead.",
line: 7,
column: 46,
},
],
options,
},
],
});

0 comments on commit 2fdfc55

Please sign in to comment.