/
adjacent-overload-signatures.js
138 lines (125 loc) · 4.16 KB
/
adjacent-overload-signatures.js
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
* @fileoverview Enforces member overloads to be consecutive.
* @author Patricio Trevino
*/
'use strict';
const util = require('../util');
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'Require that member overloads be consecutive',
category: 'TypeScript',
extraDescription: [util.tslintRule('adjacent-overload-signatures')],
url: util.metaDocsUrl('adjacent-overload-signatures'),
recommended: 'error'
},
schema: [],
messages: {
adjacentSignature: "All '{{name}}' signatures should be adjacent."
}
},
create(context) {
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
/**
* Gets the name of the member being processed.
* @param {ASTNode} member the member being processed.
* @returns {string|null} the name of the member or null if it's a member not relevant to the rule.
* @private
*/
function getMemberName(member) {
if (!member) return null;
switch (member.type) {
case 'ExportDefaultDeclaration':
case 'ExportNamedDeclaration': {
// export statements (e.g. export { a };)
// have no declarations, so ignore them
return member.declaration ? getMemberName(member.declaration) : null;
}
case 'TSDeclareFunction':
case 'FunctionDeclaration':
case 'TSNamespaceFunctionDeclaration': {
return member.id && member.id.name;
}
case 'TSMethodSignature': {
return (
(member.key && (member.key.name || member.key.value)) ||
(member.name && (member.name.name || member.name.value))
);
}
case 'TSCallSignatureDeclaration': {
return 'call';
}
case 'TSConstructSignatureDeclaration': {
return 'new';
}
case 'MethodDefinition': {
return member.key.name || member.key.value;
}
default: {
return null;
}
}
}
/**
* Determine whether two methods are the same or not
* @param {{ name: string; static: boolean }} method1 a method to compare
* @param {{ name: string; static: boolean }} method2 another method to compare with
* @returns {boolean} true if two methods are the same
* @private
*/
function isSameMethod(method1, method2) {
return method1.name === method2.name && method1.static === method2.static;
}
/**
* Check the body for overload methods.
* @param {ASTNode} node the body to be inspected.
* @returns {void}
* @private
*/
function checkBodyForOverloadMethods(node) {
const members = node.body || node.members;
if (members) {
let lastMethod;
const seenMethods = [];
members.forEach(member => {
const name = getMemberName(member);
const method = {
name,
static: member.static
};
const index = seenMethods.findIndex(seenMethod =>
isSameMethod(method, seenMethod)
);
if (index > -1 && !isSameMethod(method, lastMethod)) {
context.report({
node: member,
messageId: 'adjacentSignature',
data: {
name: (method.static ? 'static ' : '') + method.name
}
});
} else if (name && index === -1) {
seenMethods.push(method);
}
lastMethod = method;
});
}
}
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
return {
TSModuleBlock: checkBodyForOverloadMethods,
TSTypeLiteral: checkBodyForOverloadMethods,
TSInterfaceBody: checkBodyForOverloadMethods,
ClassBody: checkBodyForOverloadMethods,
Program: checkBodyForOverloadMethods
};
}
};