Add `no-unnecessary-generics` rule

This commit is contained in:
Andy Hanson 2017-08-31 14:42:01 -07:00
Родитель 1fe792d227
Коммит 69d1595d49
5 изменённых файлов: 184 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,62 @@
# no-unnecessary-generics
Forbids a function to use a generic type parameter only once.
Generic type parameters allow you to relate the type of one thing to another;
if they are used only once, they can be replaced with their type constraint.
**Bad**:
```ts
function logAnything<T>(x: T): void;
```
**Good**:
```ts
function logAnything(x: any): void;
```
---
**Bad**:
```ts
function useLogger<T extends Logger>(logger: T): void;
```
**Good**:
```ts
function useLogger(logger: Logger): void;
```
---
**Bad**:
```ts
function clear<T>(array: T[]): void;
```
**Good**:
```ts
function clear(array: any[]): void;
```
---
**Bad**:
```ts
function parse<T>(): T;
const x = parse<number>();
```
**Good**:
```ts
function parse(): {};
const x = parse() as number;
```

Просмотреть файл

@ -12,6 +12,7 @@
"strict-export-declare-modifiers": true,
"no-any-union": true,
"no-single-declare-module": true,
"no-unnecessary-generics": true,
"no-useless-files": true,
"prefer-declare-function": true,
"trim-file": true,

Просмотреть файл

@ -0,0 +1,86 @@
import * as Lint from "tslint";
import * as ts from "typescript";
import { failure } from "../util";
export class Rule extends Lint.Rules.AbstractRule {
static metadata: Lint.IRuleMetadata = {
ruleName: "no-unnecessary-generics",
description: "Forbids signatures using a generic parameter only once.",
optionsDescription: "Not configurable.",
options: null,
type: "style",
typescriptOnly: true,
};
static FAILURE_STRING(typeParameter: string) {
return failure(
Rule.metadata.ruleName,
`Type parameter ${typeParameter} is used only once.`);
}
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
}
}
function walk(ctx: Lint.WalkContext<void>): void {
const { sourceFile } = ctx;
sourceFile.forEachChild(function cb(node) {
if (ts.isFunctionLike(node)) {
checkSignature(node);
}
node.forEachChild(cb);
});
function checkSignature(sig: ts.SignatureDeclaration) {
if (!sig.typeParameters) {
return;
}
for (const tp of sig.typeParameters) {
const typeParameter = tp.name.text;
const soleUse = getSoleUse(sig, typeParameter);
if (soleUse !== undefined) {
ctx.addFailureAtNode(soleUse, Rule.FAILURE_STRING(typeParameter));
}
}
}
}
function getSoleUse(sig: ts.SignatureDeclaration, typeParameter: string): ts.Identifier | undefined {
const exit = {};
let soleUse: ts.Identifier | undefined;
try {
for (const param of sig.parameters) {
if (param.type) {
recur(param.type);
}
}
if (sig.type) {
recur(sig.type);
}
} catch (err) {
if (err === exit) {
return undefined;
}
throw err;
}
return soleUse;
function recur(node: ts.TypeNode) {
if (ts.isIdentifier(node)) {
if (node.text === typeParameter) {
if (soleUse === undefined) {
soleUse = node;
} else {
throw exit;
}
}
} else {
node.forEachChild(recur);
}
}
}

Просмотреть файл

@ -0,0 +1,29 @@
interface I {
<T>(value: T): void;
~ [0]
m<T>(x: T): void;
~ [0]
}
class C {
constructor<T>(x: T) {}
~ [0]
}
type Fn = <T>() => T;
~ [0]
type Ctr = new<T>() => T;
~ [0]
function f<T>(): T { }
~ [0]
const f = function<T>(): T {};
~ [0]
const f2 = <T>(): T => {};
~ [0]
// OK:
function foo<T>(m: Map<T, T>): void {}
[0]: Type parameter T is used only once. See: https://github.com/Microsoft/dtslint/blob/master/docs/no-unnecessary-generics.md

Просмотреть файл

@ -0,0 +1,6 @@
{
"rulesDirectory": ["../../bin/rules"],
"rules": {
"no-unnecessary-generics": true
}
}