Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): [member-ordering] add index signature #1190

Merged
merged 2 commits into from Dec 3, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ export 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