Skip to content

Commit

Permalink
fix: also support type references and super class expressions in rule…
Browse files Browse the repository at this point in the history
… type-parameter-spacing (#1)
  • Loading branch information
atheck committed Aug 14, 2022
1 parent a477f4a commit b5ef4f7
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 26 deletions.
10 changes: 9 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function myFunc (parameter: number [] []) {

### type-parameter-spacing

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

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

Expand All @@ -148,6 +148,8 @@ function generic <TType> (parameter: TType) {
interface Generic <TType> {
value: TType,
}

const value: GenericType <number>;
~~~

👍 Examples of **correct** code for this rule:
Expand All @@ -160,6 +162,8 @@ function generic<TType> (parameter: TType) {
interface Generic<TType> {
value: TType,
}

const value: GenericType<number>;
~~~

**always:**
Expand All @@ -174,6 +178,8 @@ function generic<TType> (parameter: TType) {
interface Generic<TType> {
value: TType,
}

const value: GenericType<number>;
~~~

👍 Examples of **correct** code for this rule:
Expand All @@ -186,4 +192,6 @@ function generic <TType> (parameter: TType) {
interface Generic <TType> {
value: TType,
}

const value: GenericType <number>;
~~~
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { rules } from "./rules";

// See https://astexplorer.net/

export {
rules,
};
34 changes: 19 additions & 15 deletions src/rules/type-parameter-spacing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,40 +44,44 @@ const typeParameterSpacing = createRule<Options, MessageIds>({
return {
/* eslint-disable @typescript-eslint/naming-convention */
FunctionDeclaration (node: TSESTree.FunctionDeclaration) {
handleNode(node, context, mode);
handleNode({ identifierNode: node.id, typeParametersNode: node.typeParameters }, context, mode);
},
TSDeclareFunction (node: TSESTree.TSDeclareFunction) {
handleNode(node, context, mode);
handleNode({ identifierNode: node.id, typeParametersNode: node.typeParameters }, context, mode);
},
TSInterfaceDeclaration (node: TSESTree.TSInterfaceDeclaration) {
handleNode(node, context, mode);
handleNode({ identifierNode: node.id, typeParametersNode: node.typeParameters }, context, mode);
},
TSTypeAliasDeclaration (node: TSESTree.TSTypeAliasDeclaration) {
handleNode(node, context, mode);
handleNode({ identifierNode: node.id, typeParametersNode: node.typeParameters }, context, mode);
},
ClassDeclaration (node: TSESTree.ClassDeclaration) {
handleNode(node, context, mode);
handleNode({ identifierNode: node.id, typeParametersNode: node.typeParameters }, context, mode);
handleNode({ identifierNode: node.superClass, typeParametersNode: node.superTypeParameters }, context, mode);
},
TSTypeReference (node: TSESTree.TSTypeReference) {
handleNode({ identifierNode: node.typeName, typeParametersNode: node.typeParameters }, context, mode);
},
/* eslint-enable @typescript-eslint/naming-convention */
};
},
});

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

const identifierEndIndex = node.id.range[1];
const typeParametersStartIndex = node.typeParameters.range[0];
const identifierEndIndex = identifierNode.range[1];
const typeParametersStartIndex = typeParametersNode.range[0];

switch (mode) {
case "always":
if (identifierEndIndex === typeParametersStartIndex) {
context.report({
messageId: "oneSpace",
node,
fix: fixForAlways(node.id),
node: typeParametersNode,
fix: fixForAlways(identifierNode),
});
}
break;
Expand All @@ -86,19 +90,19 @@ function handleNode<TNode extends TSESTree.Node> (node: TNode & { id: TSESTree.I
if (identifierEndIndex < typeParametersStartIndex) {
context.report({
messageId: "noSpace",
node,
fix: fixForNever(node.id, node.typeParameters),
node: typeParametersNode,
fix: fixForNever(identifierNode, typeParametersNode),
});
}
break;
}
}

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

function fixForAlways (node: TSESTree.Identifier): ReportFixFunction {
function fixForAlways (node: TSESTree.Node): ReportFixFunction {
return (fixer: RuleFixer): RuleFix => fixer.insertTextAfter(node, " ");
}

Expand Down
168 changes: 158 additions & 10 deletions test/rules/type-parameter-spacing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ ruleTester.run(`${ruleName} for function declaration with mode 'never'`, typePar
errors: [
{
messageId: "noSpace",
type: AST_NODE_TYPES.FunctionDeclaration,
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
},
],
output: "function generic<TType> () {}",
Expand All @@ -41,7 +41,7 @@ ruleTester.run(`${ruleName} for function declaration with mode 'always'`, typePa
errors: [
{
messageId: "oneSpace",
type: AST_NODE_TYPES.FunctionDeclaration,
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
},
],
output: "function generic <TType> () {}",
Expand All @@ -63,7 +63,7 @@ ruleTester.run(`${ruleName} for declare function with mode 'never'`, typeParamet
errors: [
{
messageId: "noSpace",
type: AST_NODE_TYPES.TSDeclareFunction,
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
},
],
output: "declare function generic<TType> (): void;",
Expand All @@ -85,7 +85,7 @@ ruleTester.run(`${ruleName} for declare function with mode 'always'`, typeParame
errors: [
{
messageId: "oneSpace",
type: AST_NODE_TYPES.TSDeclareFunction,
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
},
],
output: "declare function generic <TType> (): void;",
Expand All @@ -107,7 +107,7 @@ ruleTester.run(`${ruleName} for interface with mode 'never'`, typeParameterSpaci
errors: [
{
messageId: "noSpace",
type: AST_NODE_TYPES.TSInterfaceDeclaration,
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
},
],
output: "interface Generic<TType> {}",
Expand All @@ -129,7 +129,7 @@ ruleTester.run(`${ruleName} for interface with mode 'always'`, typeParameterSpac
errors: [
{
messageId: "oneSpace",
type: AST_NODE_TYPES.TSInterfaceDeclaration,
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
},
],
output: "interface Generic <TType> {}",
Expand All @@ -151,7 +151,7 @@ ruleTester.run(`${ruleName} for type with mode 'never'`, typeParameterSpacing, {
errors: [
{
messageId: "noSpace",
type: AST_NODE_TYPES.TSTypeAliasDeclaration,
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
},
],
output: "type Generic<TType> = {};",
Expand All @@ -173,7 +173,7 @@ ruleTester.run(`${ruleName} for type with mode 'always'`, typeParameterSpacing,
errors: [
{
messageId: "oneSpace",
type: AST_NODE_TYPES.TSTypeAliasDeclaration,
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
},
],
output: "type Generic <TType> = {};",
Expand All @@ -195,7 +195,7 @@ ruleTester.run(`${ruleName} for class with mode 'never'`, typeParameterSpacing,
errors: [
{
messageId: "noSpace",
type: AST_NODE_TYPES.ClassDeclaration,
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
},
],
output: "class Generic<TType> {};",
Expand All @@ -217,10 +217,158 @@ ruleTester.run(`${ruleName} for class with mode 'always'`, typeParameterSpacing,
errors: [
{
messageId: "oneSpace",
type: AST_NODE_TYPES.ClassDeclaration,
type: AST_NODE_TYPES.TSTypeParameterDeclaration,
},
],
output: "class Generic <TType> {};",
},
],
});

ruleTester.run(`${ruleName} for type references with mode 'never'`, typeParameterSpacing, {
valid: [
{
options: ["never"],
code: "function test (): GenericType<number> {}",
},
{
options: ["never"],
code: "type Alias = GenericType<number>;",
},
{
options: ["never"],
code: "const value: GenericType<number>;",
},
],
invalid: [
{
options: ["never"],
code: "function test (): GenericType <number> {}",
errors: [
{
messageId: "noSpace",
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
},
],
output: "function test (): GenericType<number> {}",
},
{
options: ["never"],
code: "type Alias = GenericType <number>;",
errors: [
{
messageId: "noSpace",
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
},
],
output: "type Alias = GenericType<number>;",
},
{
options: ["never"],
code: "const value: GenericType <number>;",
errors: [
{
messageId: "noSpace",
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
},
],
output: "const value: GenericType<number>;",
},
],
});

ruleTester.run(`${ruleName} for type references with mode 'always'`, typeParameterSpacing, {
valid: [
{
options: ["always"],
code: "function test (): GenericType <number> {}",
},
{
options: ["always"],
code: "type Alias = GenericType <number>;",
},
{
options: ["always"],
code: "const value: GenericType <number>;",
},
],
invalid: [
{
options: ["always"],
code: "function test (): GenericType<number> {}",
errors: [
{
messageId: "oneSpace",
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
},
],
output: "function test (): GenericType <number> {}",
},
{
options: ["always"],
code: "type Alias = GenericType<number>;",
errors: [
{
messageId: "oneSpace",
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
},
],
output: "type Alias = GenericType <number>;",
},
{
options: ["always"],
code: "const value: GenericType<number>;",
errors: [
{
messageId: "oneSpace",
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
},
],
output: "const value: GenericType <number>;",
},
],
});

ruleTester.run(`${ruleName} for super class with mode 'never'`, typeParameterSpacing, {
valid: [
{
options: ["never"],
code: "class Extends extends SuperClass<number> {};",
},
],
invalid: [
{
options: ["never"],
code: "class Extends extends SuperClass <number> {};",
errors: [
{
messageId: "noSpace",
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
},
],
output: "class Extends extends SuperClass<number> {};",
},
],
});

ruleTester.run(`${ruleName} for super class with mode 'always'`, typeParameterSpacing, {
valid: [
{
options: ["always"],
code: "class Extends extends SuperClass <number> {};",
},
],
invalid: [
{
options: ["always"],
code: "class Extends extends SuperClass<number> {};",
errors: [
{
messageId: "oneSpace",
type: AST_NODE_TYPES.TSTypeParameterInstantiation,
},
],
output: "class Extends extends SuperClass <number> {};",
},
],
});

0 comments on commit b5ef4f7

Please sign in to comment.