Skip to content

Commit b5ef4f7

Browse files
committed
fix: also support type references and super class expressions in rule type-parameter-spacing (#1)
1 parent a477f4a commit b5ef4f7

File tree

4 files changed

+188
-26
lines changed

4 files changed

+188
-26
lines changed

readme.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ function myFunc (parameter: number [] []) {
121121

122122
### type-parameter-spacing
123123

124-
This rule enforces correct spacing between an identifier and type parameters. This works for functions, function declarations, interfaces, type aliases, and classes.
124+
This rule enforces correct spacing between an identifier and type parameters.
125125

126126
🔧 The `--fix` option on the command line can automatically fix the problems reported by this rule.
127127

@@ -148,6 +148,8 @@ function generic <TType> (parameter: TType) {
148148
interface Generic <TType> {
149149
value: TType,
150150
}
151+
152+
const value: GenericType <number>;
151153
~~~
152154

153155
👍 Examples of **correct** code for this rule:
@@ -160,6 +162,8 @@ function generic<TType> (parameter: TType) {
160162
interface Generic<TType> {
161163
value: TType,
162164
}
165+
166+
const value: GenericType<number>;
163167
~~~
164168

165169
**always:**
@@ -174,6 +178,8 @@ function generic<TType> (parameter: TType) {
174178
interface Generic<TType> {
175179
value: TType,
176180
}
181+
182+
const value: GenericType<number>;
177183
~~~
178184

179185
👍 Examples of **correct** code for this rule:
@@ -186,4 +192,6 @@ function generic <TType> (parameter: TType) {
186192
interface Generic <TType> {
187193
value: TType,
188194
}
195+
196+
const value: GenericType <number>;
189197
~~~

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { rules } from "./rules";
22

3+
// See https://astexplorer.net/
4+
35
export {
46
rules,
57
};

src/rules/type-parameter-spacing.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,40 +44,44 @@ const typeParameterSpacing = createRule<Options, MessageIds>({
4444
return {
4545
/* eslint-disable @typescript-eslint/naming-convention */
4646
FunctionDeclaration (node: TSESTree.FunctionDeclaration) {
47-
handleNode(node, context, mode);
47+
handleNode({ identifierNode: node.id, typeParametersNode: node.typeParameters }, context, mode);
4848
},
4949
TSDeclareFunction (node: TSESTree.TSDeclareFunction) {
50-
handleNode(node, context, mode);
50+
handleNode({ identifierNode: node.id, typeParametersNode: node.typeParameters }, context, mode);
5151
},
5252
TSInterfaceDeclaration (node: TSESTree.TSInterfaceDeclaration) {
53-
handleNode(node, context, mode);
53+
handleNode({ identifierNode: node.id, typeParametersNode: node.typeParameters }, context, mode);
5454
},
5555
TSTypeAliasDeclaration (node: TSESTree.TSTypeAliasDeclaration) {
56-
handleNode(node, context, mode);
56+
handleNode({ identifierNode: node.id, typeParametersNode: node.typeParameters }, context, mode);
5757
},
5858
ClassDeclaration (node: TSESTree.ClassDeclaration) {
59-
handleNode(node, context, mode);
59+
handleNode({ identifierNode: node.id, typeParametersNode: node.typeParameters }, context, mode);
60+
handleNode({ identifierNode: node.superClass, typeParametersNode: node.superTypeParameters }, context, mode);
61+
},
62+
TSTypeReference (node: TSESTree.TSTypeReference) {
63+
handleNode({ identifierNode: node.typeName, typeParametersNode: node.typeParameters }, context, mode);
6064
},
6165
/* eslint-enable @typescript-eslint/naming-convention */
6266
};
6367
},
6468
});
6569

66-
function handleNode<TNode extends TSESTree.Node> (node: TNode & { id: TSESTree.Identifier | null, typeParameters?: TSESTree.TSTypeParameterDeclaration }, context: Readonly<RuleContext<MessageIds, Options>>, mode: Config): void {
67-
if (!node.id || !node.typeParameters) {
70+
function handleNode ({ identifierNode, typeParametersNode }: { identifierNode: TSESTree.Node | null, typeParametersNode: TSESTree.Node | undefined }, context: Readonly<RuleContext<MessageIds, Options>>, mode: Config): void {
71+
if (!identifierNode || !typeParametersNode) {
6872
return;
6973
}
7074

71-
const identifierEndIndex = node.id.range[1];
72-
const typeParametersStartIndex = node.typeParameters.range[0];
75+
const identifierEndIndex = identifierNode.range[1];
76+
const typeParametersStartIndex = typeParametersNode.range[0];
7377

7478
switch (mode) {
7579
case "always":
7680
if (identifierEndIndex === typeParametersStartIndex) {
7781
context.report({
7882
messageId: "oneSpace",
79-
node,
80-
fix: fixForAlways(node.id),
83+
node: typeParametersNode,
84+
fix: fixForAlways(identifierNode),
8185
});
8286
}
8387
break;
@@ -86,19 +90,19 @@ function handleNode<TNode extends TSESTree.Node> (node: TNode & { id: TSESTree.I
8690
if (identifierEndIndex < typeParametersStartIndex) {
8791
context.report({
8892
messageId: "noSpace",
89-
node,
90-
fix: fixForNever(node.id, node.typeParameters),
93+
node: typeParametersNode,
94+
fix: fixForNever(identifierNode, typeParametersNode),
9195
});
9296
}
9397
break;
9498
}
9599
}
96100

97-
function fixForNever (identifier: TSESTree.Identifier, typeParameters: TSESTree.TSTypeParameterDeclaration): ReportFixFunction {
101+
function fixForNever (identifier: TSESTree.Node, typeParameters: TSESTree.Node): ReportFixFunction {
98102
return (fixer: RuleFixer): RuleFix => fixer.removeRange([identifier.range[1], typeParameters.range[0]]);
99103
}
100104

101-
function fixForAlways (node: TSESTree.Identifier): ReportFixFunction {
105+
function fixForAlways (node: TSESTree.Node): ReportFixFunction {
102106
return (fixer: RuleFixer): RuleFix => fixer.insertTextAfter(node, " ");
103107
}
104108

test/rules/type-parameter-spacing.spec.ts

Lines changed: 158 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ ruleTester.run(`${ruleName} for function declaration with mode 'never'`, typePar
1919
errors: [
2020
{
2121
messageId: "noSpace",
22-
type: AST_NODE_TYPES.FunctionDeclaration,
22+
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
2323
},
2424
],
2525
output: "function generic<TType> () {}",
@@ -41,7 +41,7 @@ ruleTester.run(`${ruleName} for function declaration with mode 'always'`, typePa
4141
errors: [
4242
{
4343
messageId: "oneSpace",
44-
type: AST_NODE_TYPES.FunctionDeclaration,
44+
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
4545
},
4646
],
4747
output: "function generic <TType> () {}",
@@ -63,7 +63,7 @@ ruleTester.run(`${ruleName} for declare function with mode 'never'`, typeParamet
6363
errors: [
6464
{
6565
messageId: "noSpace",
66-
type: AST_NODE_TYPES.TSDeclareFunction,
66+
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
6767
},
6868
],
6969
output: "declare function generic<TType> (): void;",
@@ -85,7 +85,7 @@ ruleTester.run(`${ruleName} for declare function with mode 'always'`, typeParame
8585
errors: [
8686
{
8787
messageId: "oneSpace",
88-
type: AST_NODE_TYPES.TSDeclareFunction,
88+
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
8989
},
9090
],
9191
output: "declare function generic <TType> (): void;",
@@ -107,7 +107,7 @@ ruleTester.run(`${ruleName} for interface with mode 'never'`, typeParameterSpaci
107107
errors: [
108108
{
109109
messageId: "noSpace",
110-
type: AST_NODE_TYPES.TSInterfaceDeclaration,
110+
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
111111
},
112112
],
113113
output: "interface Generic<TType> {}",
@@ -129,7 +129,7 @@ ruleTester.run(`${ruleName} for interface with mode 'always'`, typeParameterSpac
129129
errors: [
130130
{
131131
messageId: "oneSpace",
132-
type: AST_NODE_TYPES.TSInterfaceDeclaration,
132+
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
133133
},
134134
],
135135
output: "interface Generic <TType> {}",
@@ -151,7 +151,7 @@ ruleTester.run(`${ruleName} for type with mode 'never'`, typeParameterSpacing, {
151151
errors: [
152152
{
153153
messageId: "noSpace",
154-
type: AST_NODE_TYPES.TSTypeAliasDeclaration,
154+
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
155155
},
156156
],
157157
output: "type Generic<TType> = {};",
@@ -173,7 +173,7 @@ ruleTester.run(`${ruleName} for type with mode 'always'`, typeParameterSpacing,
173173
errors: [
174174
{
175175
messageId: "oneSpace",
176-
type: AST_NODE_TYPES.TSTypeAliasDeclaration,
176+
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
177177
},
178178
],
179179
output: "type Generic <TType> = {};",
@@ -195,7 +195,7 @@ ruleTester.run(`${ruleName} for class with mode 'never'`, typeParameterSpacing,
195195
errors: [
196196
{
197197
messageId: "noSpace",
198-
type: AST_NODE_TYPES.ClassDeclaration,
198+
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
199199
},
200200
],
201201
output: "class Generic<TType> {};",
@@ -217,10 +217,158 @@ ruleTester.run(`${ruleName} for class with mode 'always'`, typeParameterSpacing,
217217
errors: [
218218
{
219219
messageId: "oneSpace",
220-
type: AST_NODE_TYPES.ClassDeclaration,
220+
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
221221
},
222222
],
223223
output: "class Generic <TType> {};",
224224
},
225225
],
226+
});
227+
228+
ruleTester.run(`${ruleName} for type references with mode 'never'`, typeParameterSpacing, {
229+
valid: [
230+
{
231+
options: ["never"],
232+
code: "function test (): GenericType<number> {}",
233+
},
234+
{
235+
options: ["never"],
236+
code: "type Alias = GenericType<number>;",
237+
},
238+
{
239+
options: ["never"],
240+
code: "const value: GenericType<number>;",
241+
},
242+
],
243+
invalid: [
244+
{
245+
options: ["never"],
246+
code: "function test (): GenericType <number> {}",
247+
errors: [
248+
{
249+
messageId: "noSpace",
250+
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
251+
},
252+
],
253+
output: "function test (): GenericType<number> {}",
254+
},
255+
{
256+
options: ["never"],
257+
code: "type Alias = GenericType <number>;",
258+
errors: [
259+
{
260+
messageId: "noSpace",
261+
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
262+
},
263+
],
264+
output: "type Alias = GenericType<number>;",
265+
},
266+
{
267+
options: ["never"],
268+
code: "const value: GenericType <number>;",
269+
errors: [
270+
{
271+
messageId: "noSpace",
272+
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
273+
},
274+
],
275+
output: "const value: GenericType<number>;",
276+
},
277+
],
278+
});
279+
280+
ruleTester.run(`${ruleName} for type references with mode 'always'`, typeParameterSpacing, {
281+
valid: [
282+
{
283+
options: ["always"],
284+
code: "function test (): GenericType <number> {}",
285+
},
286+
{
287+
options: ["always"],
288+
code: "type Alias = GenericType <number>;",
289+
},
290+
{
291+
options: ["always"],
292+
code: "const value: GenericType <number>;",
293+
},
294+
],
295+
invalid: [
296+
{
297+
options: ["always"],
298+
code: "function test (): GenericType<number> {}",
299+
errors: [
300+
{
301+
messageId: "oneSpace",
302+
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
303+
},
304+
],
305+
output: "function test (): GenericType <number> {}",
306+
},
307+
{
308+
options: ["always"],
309+
code: "type Alias = GenericType<number>;",
310+
errors: [
311+
{
312+
messageId: "oneSpace",
313+
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
314+
},
315+
],
316+
output: "type Alias = GenericType <number>;",
317+
},
318+
{
319+
options: ["always"],
320+
code: "const value: GenericType<number>;",
321+
errors: [
322+
{
323+
messageId: "oneSpace",
324+
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
325+
},
326+
],
327+
output: "const value: GenericType <number>;",
328+
},
329+
],
330+
});
331+
332+
ruleTester.run(`${ruleName} for super class with mode 'never'`, typeParameterSpacing, {
333+
valid: [
334+
{
335+
options: ["never"],
336+
code: "class Extends extends SuperClass<number> {};",
337+
},
338+
],
339+
invalid: [
340+
{
341+
options: ["never"],
342+
code: "class Extends extends SuperClass <number> {};",
343+
errors: [
344+
{
345+
messageId: "noSpace",
346+
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
347+
},
348+
],
349+
output: "class Extends extends SuperClass<number> {};",
350+
},
351+
],
352+
});
353+
354+
ruleTester.run(`${ruleName} for super class with mode 'always'`, typeParameterSpacing, {
355+
valid: [
356+
{
357+
options: ["always"],
358+
code: "class Extends extends SuperClass <number> {};",
359+
},
360+
],
361+
invalid: [
362+
{
363+
options: ["always"],
364+
code: "class Extends extends SuperClass<number> {};",
365+
errors: [
366+
{
367+
messageId: "oneSpace",
368+
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
369+
},
370+
],
371+
output: "class Extends extends SuperClass <number> {};",
372+
},
373+
],
226374
});

0 commit comments

Comments
 (0)