Navigation Menu

Skip to content

Commit

Permalink
feat(eslint-plugin): [member-ordering] add index signature (#1190)
Browse files Browse the repository at this point in the history
Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
  • Loading branch information
sveyret and bradzacher committed Dec 3, 2019
1 parent 39f654b commit b5a52a3
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 89 deletions.
70 changes: 61 additions & 9 deletions packages/eslint-plugin/docs/rules/member-ordering.md
Expand Up @@ -17,8 +17,8 @@ It allows to group members by their type (e.g. `public-static-field`, `protected
classes?: Array<MemberType> | never
classExpressions?: Array<MemberType> | never

interfaces?: ['field' | 'method' | 'constructor'] | never
typeLiterals?: ['field' | 'method' | 'constructor'] | never
interfaces?: ['signature' | 'field' | 'method' | 'constructor'] | never
typeLiterals?: ['signature' | 'field' | 'method' | 'constructor'] | never
}
```

Expand All @@ -30,6 +30,9 @@ There are multiple ways to specify the member types. The most explicit and granu

```json5
[
// Index signature
'signature',

// Fields
'public-static-field',
'protected-static-field',
Expand Down Expand Up @@ -67,6 +70,9 @@ It is also possible to group member types by their accessibility (`static`, `ins

```json5
[
// Index signature
// No accessibility for index signature. See above.

// Fields
'public-field', // = ['public-static-field', 'public-instance-field'])
'protected-field', // = ['protected-static-field', 'protected-instance-field'])
Expand All @@ -88,6 +94,9 @@ Another option is to group the member types by their scope (`public`, `protected

```json5
[
// Index signature
// No scope for index signature. See above.

// Fields
'static-field', // = ['public-static-field', 'protected-static-field', 'private-static-field'])
'instance-field', // = ['public-instance-field', 'protected-instance-field', 'private-instance-field'])
Expand All @@ -109,6 +118,9 @@ The third grouping option is to ignore both scope and accessibility.

```json5
[
// Index signature
// No grouping for index signature. See above.

// Fields
'field', // = ['public-static-field', 'protected-static-field', 'private-static-field', 'public-instance-field', 'protected-instance-field', 'private-instance-field',
// 'public-abstract-field', 'protected-abstract-field', private-abstract-field'])
Expand All @@ -129,6 +141,8 @@ The default configuration looks as follows:
```json
{
"default": [
"signature",

"public-static-field",
"protected-static-field",
"private-static-field",
Expand Down Expand Up @@ -186,7 +200,7 @@ Note: The default configuration contains member group types which contain other

Note: The `default` options are overwritten in these examples.

#### Configuration: `{ "default": ["method", "constructor", "field"] }`
#### Configuration: `{ "default": ["signature", "method", "constructor", "field"] }`

##### Incorrect examples

Expand All @@ -197,6 +211,8 @@ interface Foo {
new (); // -> constructor

A(): void; // -> method

[Z: string]: any; // -> signature
}
```

Expand All @@ -209,6 +225,8 @@ type Foo = {
// no constructor

A(): void; // -> method

// no signature
};
```

Expand All @@ -224,10 +242,12 @@ class Foo {

public static A(): void {} // -> method
public B(): void {} // -> method

[Z: string]: any; // -> signature
}
```

Note: Accessibility or scope are ignored with this ignored.
Note: Accessibility or scope are ignored with this configuration.

```ts
const Foo = class {
Expand All @@ -239,6 +259,8 @@ const Foo = class {
public static A(): void {} // -> method
public B(): void {} // -> method

[Z: string]: any; // -> signature

protected static E: string; // -> field
};
```
Expand All @@ -249,6 +271,8 @@ Note: Not all members have to be grouped to find rule violations.

```ts
interface Foo {
[Z: string]: any; // -> signature

A(): void; // -> method

new (); // -> constructor
Expand All @@ -259,6 +283,8 @@ interface Foo {

```ts
type Foo = {
// no signature

A(): void; // -> method

// no constructor
Expand All @@ -269,6 +295,8 @@ type Foo = {

```ts
class Foo {
[Z: string]: any; // -> signature

public static A(): void {} // -> method
public B(): void {} // -> method

Expand All @@ -282,6 +310,8 @@ class Foo {

```ts
const Foo = class {
[Z: string]: any; // -> signature

public static A(): void {} // -> method
public B(): void {} // -> method

Expand Down Expand Up @@ -311,6 +341,8 @@ class Foo {

public static A(): void {} // (irrelevant)

[Z: string]: any; // (irrelevant)

public B(): void {} // -> public instance method
}
```
Expand All @@ -321,6 +353,8 @@ Note: Public instance methods should come first before public static fields. Eve
const Foo = class {
private C: string; // (irrelevant)

[Z: string]: any; // (irrelevant)

public static E: string; // -> public static field

public D: string; // (irrelevant)
Expand Down Expand Up @@ -350,6 +384,8 @@ class Foo {
constructor() {} // (irrelevant)

public static A(): void {} // (irrelevant)

[Z: string]: any; // (irrelevant)
}
```

Expand All @@ -359,6 +395,8 @@ const Foo = class {

private C: string; // (irrelevant)

[Z: string]: any; // (irrelevant)

public D: string; // (irrelevant)

constructor() {} // (irrelevant)
Expand All @@ -384,6 +422,8 @@ class Foo {
private static D: string; // -> static field

public static A: string; // -> public static field

[Z: string]: any; // (irrelevant)
}
```

Expand All @@ -402,6 +442,8 @@ const foo = class {
protected static C: string; // -> static field
private static D: string; // -> static field

[Z: string]: any; // (irrelevant)

public static A: string; // -> public static field
};
```
Expand All @@ -424,6 +466,8 @@ class Foo {

```ts
const foo = class {
[Z: string]: any; // -> signature

public static A: string; // -> public static field

constructor() {} // -> constructor
Expand Down Expand Up @@ -596,11 +640,11 @@ const foo = class {

Note: If this is not set, the `default` will automatically be applied to classes expressions as well. If a `interfaces` configuration is provided, only this configuration will be used for `interfaces` (i.e. nothing will be merged with `default`).

Note: The configuration for `interfaces` only allows a limited set of member types: `field`, `constructor` and `method`.
Note: The configuration for `interfaces` only allows a limited set of member types: `signature`, `field`, `constructor` and `method`.

Note: The configuration for `interfaces` does not apply to type literals (use `typeLiterals` for them).

#### Configuration: `{ "interfaces": ["method", "constructor", "field"] }`
#### Configuration: `{ "interfaces": ["signature", "method", "constructor", "field"] }`

##### Incorrect example

Expand All @@ -611,13 +655,17 @@ interface Foo {
new (); // -> constructor

A(): void; // -> method

[Z: string]: any; // -> signature
}
```

##### Correct example

```ts
interface Foo {
[Z: string]: any; // -> signature

A(): void; // -> method

new (); // -> constructor
Expand All @@ -630,11 +678,11 @@ interface Foo {

Note: If this is not set, the `default` will automatically be applied to classes expressions as well. If a `typeLiterals` configuration is provided, only this configuration will be used for `typeLiterals` (i.e. nothing will be merged with `default`).

Note: The configuration for `typeLiterals` only allows a limited set of member types: `field`, `constructor` and `method`.
Note: The configuration for `typeLiterals` only allows a limited set of member types: `signature`, `field`, `constructor` and `method`.

Note: The configuration for `typeLiterals` does not apply to type literals (use `interfaces` for them).
Note: The configuration for `typeLiterals` does not apply to interfaces (use `interfaces` for them).

#### Configuration: `{ "typeLiterals": ["method", "constructor", "field"] }`
#### Configuration: `{ "typeLiterals": ["signature", "method", "constructor", "field"] }`

##### Incorrect example

Expand All @@ -645,13 +693,17 @@ type Foo = {
A(): void; // -> method

new (); // -> constructor

[Z: string]: any; // -> signature
};
```

##### Correct example

```ts
type Foo = {
[Z: string]: any; // -> signature

A(): void; // -> method

new (); // -> constructor
Expand Down
12 changes: 9 additions & 3 deletions packages/eslint-plugin/src/rules/member-ordering.ts
Expand Up @@ -39,6 +39,7 @@ const allMemberTypes = ['field', 'method', 'constructor'].reduce<string[]>(
},
[],
);
allMemberTypes.unshift('signature');

export default util.createRule<Options, MessageIds>({
name: 'member-ordering',
Expand Down Expand Up @@ -104,7 +105,7 @@ export default util.createRule<Options, MessageIds>({
{
type: 'array',
items: {
enum: ['field', 'method', 'constructor'],
enum: ['signature', 'field', 'method', 'constructor'],
},
},
],
Expand All @@ -117,7 +118,7 @@ export default util.createRule<Options, MessageIds>({
{
type: 'array',
items: {
enum: ['field', 'method', 'constructor'],
enum: ['signature', 'field', 'method', 'constructor'],
},
},
],
Expand All @@ -130,6 +131,8 @@ export default util.createRule<Options, MessageIds>({
defaultOptions: [
{
default: [
'signature',

'public-static-field',
'protected-static-field',
'private-static-field',
Expand Down Expand Up @@ -194,7 +197,6 @@ export default util.createRule<Options, MessageIds>({
node: TSESTree.ClassElement | TSESTree.TypeElement,
): string | null {
// TODO: add missing TSCallSignatureDeclaration
// TODO: add missing TSIndexSignature
switch (node.type) {
case AST_NODE_TYPES.TSAbstractMethodDefinition:
case AST_NODE_TYPES.MethodDefinition:
Expand All @@ -210,6 +212,8 @@ export default util.createRule<Options, MessageIds>({
: 'field';
case AST_NODE_TYPES.TSPropertySignature:
return 'field';
case AST_NODE_TYPES.TSIndexSignature:
return 'signature';
default:
return null;
}
Expand All @@ -235,6 +239,8 @@ export default util.createRule<Options, MessageIds>({
: util.getNameFromClassMember(node, sourceCode);
case AST_NODE_TYPES.TSConstructSignatureDeclaration:
return 'new';
case AST_NODE_TYPES.TSIndexSignature:
return util.getNameFromIndexSignature(node);
default:
return null;
}
Expand Down
13 changes: 13 additions & 0 deletions packages/eslint-plugin/src/util/misc.ts
Expand Up @@ -48,6 +48,19 @@ type InferMessageIdsTypeFromRule<T> = T extends TSESLint.RuleModule<
? TMessageIds
: unknown;

/**
* Gets a string representation of the name of the index signature.
*/
export function getNameFromIndexSignature(
node: TSESTree.TSIndexSignature,
): string {
const propName: TSESTree.PropertyName | undefined = node.parameters.find(
(parameter: TSESTree.Parameter): parameter is TSESTree.Identifier =>
parameter.type === AST_NODE_TYPES.Identifier,
);
return propName ? getNameFromPropertyName(propName) : '(index signature)';
}

/**
* Gets a string name representation of the given PropertyName node
*/
Expand Down

0 comments on commit b5a52a3

Please sign in to comment.