forked from palantir/tslint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
noUnnecessaryInitializerRule.ts
109 lines (97 loc) · 4.14 KB
/
noUnnecessaryInitializerRule.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/**
* @license
* Copyright 2017 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { getChildOfKind, isBindingPattern, isNodeFlagSet } from "tsutils";
import * as ts from "typescript";
import * as Lint from "../index";
export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
ruleName: "no-unnecessary-initializer",
description: "Forbids a 'var'/'let' statement or destructuring initializer to be initialized to 'undefined'.",
hasFix: true,
optionsDescription: "Not configurable.",
options: null,
optionExamples: [true],
type: "style",
typescriptOnly: false,
};
/* tslint:enable:object-literal-sort-keys */
public static FAILURE_STRING = "Unnecessary initialization to 'undefined'.";
public static FAILURE_STRING_PARAMETER =
"Use an optional parameter instead of initializing to 'undefined'. " +
"Also, the type declaration does not need to include '| undefined'.";
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
}
}
function walk(ctx: Lint.WalkContext<void>): void {
ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
switch (node.kind) {
case ts.SyntaxKind.BindingElement:
checkInitializer(node as ts.BindingElement);
break;
case ts.SyntaxKind.VariableDeclaration:
if (!isBindingPattern((node as ts.VariableDeclaration).name) && !isNodeFlagSet(node.parent!, ts.NodeFlags.Const)) {
checkInitializer(node as ts.VariableDeclaration);
}
break;
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.Constructor: {
const { parameters } = node as ts.FunctionLikeDeclaration;
parameters.forEach((parameter, i) => {
if (isUndefined(parameter.initializer)) {
if (parametersAllOptionalAfter(parameters, i)) {
// No fix since they may want to remove '| undefined' from the type.
ctx.addFailureAtNode(parameter, Rule.FAILURE_STRING_PARAMETER);
} else {
failWithFix(parameter);
}
}
});
}
}
ts.forEachChild(node, cb);
});
function checkInitializer(node: ts.VariableDeclaration | ts.BindingElement) {
if (isUndefined(node.initializer)) {
failWithFix(node);
}
}
function failWithFix(node: ts.VariableDeclaration | ts.BindingElement | ts.ParameterDeclaration) {
const fix = Lint.Replacement.deleteFromTo(
getChildOfKind(node, ts.SyntaxKind.EqualsToken)!.pos,
node.end);
ctx.addFailureAtNode(node, Rule.FAILURE_STRING, fix);
}
}
function parametersAllOptionalAfter(parameters: ReadonlyArray<ts.ParameterDeclaration>, idx: number): boolean {
for (let i = idx + 1; i < parameters.length; i++) {
if (parameters[i].questionToken !== undefined) {
return true;
}
if (parameters[i].initializer === undefined) {
return false;
}
}
return true;
}
function isUndefined(node: ts.Node | undefined): boolean {
return node !== undefined &&
node.kind === ts.SyntaxKind.Identifier &&
(node as ts.Identifier).originalKeywordKind === ts.SyntaxKind.UndefinedKeyword;
}