From 816b367a24c15cd5cbfb4ec298356f1e24c62b5f Mon Sep 17 00:00:00 2001 From: Tim Kraut Date: Fri, 27 Sep 2019 17:42:36 +0200 Subject: [PATCH] feat(eslint-plugin): implement review changes --- .../docs/rules/member-ordering.md | 126 +++--- .../src/rules/member-ordering.ts | 404 +++++++++--------- .../tests/rules/member-ordering.test.ts | 354 +++++++-------- .../src/ts-estree/ts-estree.ts | 2 - 4 files changed, 422 insertions(+), 464 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/member-ordering.md b/packages/eslint-plugin/docs/rules/member-ordering.md index 97f218021cf6..24c286c3ae90 100644 --- a/packages/eslint-plugin/docs/rules/member-ordering.md +++ b/packages/eslint-plugin/docs/rules/member-ordering.md @@ -7,41 +7,48 @@ A consistent ordering of fields, methods and constructors can make interfaces, t This rule aims to standardize the way class declarations, class expressions, interfaces and type literals are structured and ordered. ### Grouping and sorting member groups + It allows to group members by their type (e.g. `public-static-field`, `protected-static-field`, `private-static-field`, `public-instance-field`, ...) and enforce a certain order for these groups. By default, their order is the same inside `classes`, `classExpressions`, `interfaces` and `typeLiterals` (note: not all member types apply to `interfaces` and `typeLiterals`). It is possible to define the order for any of those individually or to change the default order for all of them by setting the `default` option. ### Sorting members -Besides grouping the members and sorting their groups, this rule also allows to sort the members themselves. You have 2 options: Sort all of them while ignoring their type or sort them inside of the member types (e.g. sort all fields in an interface alphabetically). -## Options -These options allow to specify how to group the members and sort their groups. +Besides grouping the members and sorting their groups, this rule also allows to sort the members themselves (e.g. `a`, `b`, `c`, ...). You have 2 options: Sort all of them while ignoring their type or sort them while respecting their types (e.g. sort all fields in an interface alphabetically). -```ts -{ - default?: Array | 'never' +## Options - classes?: Array | 'never' - classExpressions?: Array | 'never' +These options allow to specify how to group the members and sort their groups. - interfaces?: ['signature' | 'field' | 'method' | 'constructor'] | 'never' - typeLiterals?: ['signature' | 'field' | 'method' | 'constructor'] | 'never' -} -``` +- Sort groups, don't enforce member order: Use `memberTypes` +- Sort members, don't enforce group order: Use `order` +- Sort members within groups: Use `memberTypes` and `order` -If you want to enforce an alphabetic order, you have to use this form ```ts +type TypeOptions = + | { + memberTypes: Array | 'never', + order?: 'alphabetically' | 'as-written', + } + | { + order: 'alphabetically', + }; + { - default?: Array | { memberTypes?: Array | 'never', order?: 'alphabetically' } | 'never' + default?: TypeOptions, - classes?: Array | { memberTypes?: Array | 'never', order?: 'alphabetically' } | 'never' - classExpressions?: Array | { memberTypes?: Array | 'never', order?: 'alphabetically' } | 'never' + classes?: TypeOptions, + classExpressions?: TypeOptions, - interfaces?: ['field' | 'method' | 'constructor'] | { memberTypes?: Array | 'never', order?: 'alphabetically' } | 'never' - typeLiterals?: ['field' | 'method' | 'constructor'] | { memberTypes?: Array | 'never', order?: 'alphabetically' } | 'never' + interfaces?: TypeOptions<'signature' | 'field' | 'method' | 'constructor'>, + typeLiterals?: TypeOptions<'signature' | 'field' | 'method' | 'constructor'>, } ``` See below for the possible definitions of `MemberType`. +### Deprecated syntax + +Note: There is a deprecated syntax to specify the member types as an array. + ### Member types (granular form) There are multiple ways to specify the member types. The most explicit and granular form is the following: @@ -156,57 +163,63 @@ The third grouping option is to ignore both scope and accessibility. The default configuration looks as follows: -```json +```json5 { - "default": [ - "signature", + default: [ + 'signature', - "public-static-field", - "protected-static-field", - "private-static-field", + 'public-static-field', + 'protected-static-field', + 'private-static-field', - "public-instance-field", - "protected-instance-field", - "private-instance-field", + 'public-instance-field', + 'protected-instance-field', + 'private-instance-field', - "public-abstract-field", - "protected-abstract-field", - "private-abstract-field", + 'public-abstract-field', + 'protected-abstract-field', + 'private-abstract-field', - "public-field", - "protected-field", - "private-field", + 'public-field', + 'protected-field', + 'private-field', - "static-field", - "instance-field", - "abstract-field", + 'static-field', + 'instance-field', + 'abstract-field', - "field", + 'field', - "constructor", + // Constructors + 'public-constructor', + 'protected-constructor', + 'private-constructor', - "public-static-method", - "protected-static-method", - "private-static-method", + 'constructor', - "public-instance-method", - "protected-instance-method", - "private-instance-method", + // Methods + 'public-static-method', + 'protected-static-method', + 'private-static-method', - "public-abstract-method", - "protected-abstract-method", - "private-abstract-method", + 'public-instance-method', + 'protected-instance-method', + 'private-instance-method', - "public-method", - "protected-method", - "private-method", + 'public-abstract-method', + 'protected-abstract-method', + 'private-abstract-method', - "static-method", - "instance-method", - "abstract-method", + 'public-method', + 'protected-method', + 'private-method', - "method" - ] + 'static-method', + 'instance-method', + 'abstract-method', + + 'method', + ], } ``` @@ -733,9 +746,11 @@ type Foo = { ``` ### Sorting alphabetically within member groups + It is possible to sort all members within a group alphabetically. #### Configuration: `{ default: { order: 'alphabetically' } }` + This will apply the default order (see above) and enforce an alphabetic order within each group. ##### Incorrect examples @@ -779,6 +794,7 @@ interface Foo { Note: Wrong alphabetic order (should be `a(): void` --> `b(): void` --> `c(): void`). ### Sorting alphabetically while ignoring member groups + It is also possible to sort all members and ignore the member groups completely. #### Configuration: `{ default: { memberTypes: 'never', order: 'alphabetically' } }` diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index 9b2a96f5deea..14ddcc236a90 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -9,10 +9,11 @@ export type MessageIds = 'incorrectGroupOrder' | 'incorrectOrder'; interface SortedOrderConfig { memberTypes?: string[] | 'never'; - order: 'alphabetically'; + order: 'alphabetically' | 'as-written'; } type OrderConfig = string[] | SortedOrderConfig | 'never'; +type Member = TSESTree.ClassElement | TSESTree.TypeElement; export type Options = [ { @@ -24,7 +25,37 @@ export type Options = [ }, ]; -const defaultOrder = [ +const neverConfig = { + type: 'string', + enum: ['never'], +}; + +const arrayConfig = (memberTypes: string[]): object => ({ + type: 'array', + items: { + enum: memberTypes, + }, +}); + +const objectConfig = (memberTypes: string[]): object => ({ + type: 'object', + properties: { + memberTypes: { + oneOf: [arrayConfig(memberTypes), neverConfig], + }, + order: { + type: 'string', + enum: ['alphabetically', 'as-written'], + }, + }, + additionalProperties: false, +}); + +export const defaultOrder = [ + // Index signature + 'signature', + + // Fields 'public-static-field', 'protected-static-field', 'private-static-field', @@ -47,8 +78,14 @@ const defaultOrder = [ 'field', + // Constructors + 'public-constructor', + 'protected-constructor', + 'private-constructor', + 'constructor', + // Methods 'public-static-method', 'protected-static-method', 'private-static-method', @@ -72,60 +109,30 @@ const defaultOrder = [ 'method', ]; -const allMemberTypes = ['field', 'method', 'constructor'].reduce( - (all, type) => { - all.push(type); +const allMemberTypes = ['signature', 'field', 'method', 'constructor'].reduce< + string[] +>((all, type) => { + all.push(type); - ['public', 'protected', 'private'].forEach(accessibility => { + ['public', 'protected', 'private'].forEach(accessibility => { + if (type !== 'signature') { all.push(`${accessibility}-${type}`); // e.g. `public-field` + } - if (type !== 'constructor') { - // There is no `static-constructor` or `instance-constructor - ['static', 'instance'].forEach(scope => { - if (!all.includes(`${scope}-${type}`)) { - all.push(`${scope}-${type}`); - } - - all.push(`${accessibility}-${scope}-${type}`); - }); - } - }); - - return all; - }, - [], -); - -const neverConfig = { - type: 'string', - enum: ['never'], -}; - -const allMemberTypesArrayConfig = { - type: 'array', - items: { - enum: allMemberTypes, - }, -}; + if (type !== 'constructor' && type !== 'signature') { + // There is no `static-constructor` or `instance-constructor` or `abstract-constructor` + ['static', 'instance', 'abstract'].forEach(scope => { + if (!all.includes(`${scope}-${type}`)) { + all.push(`${scope}-${type}`); + } -const allMemberTypesDefaultConfig = { - type: 'string', - enum: ['never'], -}; + all.push(`${accessibility}-${scope}-${type}`); + }); + } + }); -const allMemberTypesObjectConfig = { - type: 'object', - properties: { - memberTypes: { - oneOf: [allMemberTypesDefaultConfig, allMemberTypesArrayConfig], - }, - order: { - type: 'string', - enum: ['alphabetically'], - }, - }, - additionalProperties: false, -}; + return all; +}, []); const functionExpressions = [ AST_NODE_TYPES.FunctionExpression, @@ -134,11 +141,10 @@ const functionExpressions = [ /** * Gets the node type. + * * @param node the node to be evaluated. */ -function getNodeType( - node: TSESTree.ClassElement | TSESTree.TypeElement, -): string | null { +function getNodeType(node: Member): string | null { // TODO: add missing TSCallSignatureDeclaration switch (node.type) { case AST_NODE_TYPES.TSAbstractMethodDefinition: @@ -169,7 +175,7 @@ function getNodeType( * @param sourceCode */ function getMemberName( - node: TSESTree.ClassElement | TSESTree.TypeElement, + node: Member, sourceCode: TSESLint.SourceCode, ): string | null { switch (node.type) { @@ -182,7 +188,7 @@ function getMemberName( case AST_NODE_TYPES.MethodDefinition: return node.kind === 'constructor' ? 'constructor' - : util.getNameFromMember(node, sourceCode); + : util.getNameFromMember(node, sourceCode); case AST_NODE_TYPES.TSConstructSignatureDeclaration: return 'new'; case AST_NODE_TYPES.TSIndexSignature: @@ -192,28 +198,6 @@ function getMemberName( } } -/** - * Gets the member identifier (null if there is no identifier). - * - * @param node the node to be evaluated. - * @param sourceCode - */ -function getIdentifier( - node: TSESTree.ClassElement | TSESTree.TypeElement, - sourceCode: TSESLint.SourceCode, -): string | null { - switch (node.type) { - case AST_NODE_TYPES.TSPropertySignature: - case AST_NODE_TYPES.TSMethodSignature: - case AST_NODE_TYPES.ClassProperty: - return util.getNameFromPropertyName(node.key); - case AST_NODE_TYPES.MethodDefinition: - return util.getNameFromClassMember(node, sourceCode); - default: - return null; - } -} - /** * Gets the calculated rank using the provided method definition. * The algorithm is as follows: @@ -244,7 +228,7 @@ function getRankOrder(memberGroups: string[], orderConfig: string[]): number { * @param supportsModifiers a flag indicating whether the type supports modifiers (scope or accessibility) or not. */ function getRank( - node: TSESTree.ClassElement | TSESTree.TypeElement, + node: Member, orderConfig: string[], supportsModifiers: boolean, ): number { @@ -255,16 +239,16 @@ function getRank( return orderConfig.length - 1; } - const abstract = - node.type === AST_NODE_TYPES.TSAbstractClassProperty || - node.type === AST_NODE_TYPES.TSAbstractMethodDefinition; + const abstract = + node.type === AST_NODE_TYPES.TSAbstractClassProperty || + node.type === AST_NODE_TYPES.TSAbstractMethodDefinition; - const scope = - 'static' in node && node.static - ? 'static' - : abstract - ? 'abstract' - : 'instance'; + const scope = + 'static' in node && node.static + ? 'static' + : abstract + ? 'abstract' + : 'instance'; const accessibility = 'accessibility' in node && node.accessibility ? node.accessibility @@ -345,36 +329,36 @@ export default util.createRule({ default: { oneOf: [ neverConfig, - allMemberTypesArrayConfig, - allMemberTypesObjectConfig, + arrayConfig(allMemberTypes), + objectConfig(allMemberTypes), ], }, classes: { oneOf: [ neverConfig, - allMemberTypesArrayConfig, - allMemberTypesObjectConfig, + arrayConfig(allMemberTypes), + objectConfig(allMemberTypes), ], }, classExpressions: { oneOf: [ neverConfig, - allMemberTypesArrayConfig, - allMemberTypesObjectConfig, + arrayConfig(allMemberTypes), + objectConfig(allMemberTypes), ], }, interfaces: { oneOf: [ neverConfig, - allMemberTypesArrayConfig, - allMemberTypesObjectConfig, + arrayConfig(['signature', 'field', 'method', 'constructor']), + objectConfig(['signature', 'field', 'method', 'constructor']), ], }, typeLiterals: { oneOf: [ neverConfig, - allMemberTypesArrayConfig, - allMemberTypesObjectConfig, + arrayConfig(['signature', 'field', 'method', 'constructor']), + objectConfig(['signature', 'field', 'method', 'constructor']), ], }, }, @@ -388,6 +372,94 @@ export default util.createRule({ }, ], create(context, [options]) { + /** + * Checks if the member groups are correctly sorted. + * + * @param members Members to be validated. + * @param groupOrder Group order to be validated. + * @param supportsModifiers A flag indicating whether the type supports modifiers (scope or accessibility) or not. + * + * @return Array of member groups or null if one of the groups is not correctly sorted. + */ + function checkGroupSort( + members: Member[], + groupOrder: string[], + supportsModifiers: boolean, + ): Array | null { + const previousRanks: number[] = []; + const memberGroups: Array = []; + let isCorrectlySorted = true; + + // Find first member which isn't correctly sorted + members.forEach(member => { + const rank = getRank(member, groupOrder, supportsModifiers); + const name = getMemberName(member, context.getSourceCode()); + const rankLastMember = previousRanks[previousRanks.length - 1]; + + if (rank !== -1) { + // Works for 1st item because x < undefined === false for any x (typeof string) + if (rank < rankLastMember) { + context.report({ + node: member, + messageId: 'incorrectGroupOrder', + data: { + name, + rank: getLowestRank(previousRanks, rank, groupOrder), + }, + }); + + isCorrectlySorted = false; + } else if (rank === rankLastMember) { + // Same member group --> Push to existing member group array + memberGroups[memberGroups.length - 1].push(member); + } else { + // New member group --> Create new member group array + previousRanks.push(rank); + memberGroups.push([member]); + } + } + }); + + return isCorrectlySorted ? memberGroups : null; + } + + /** + * Checks if the members are alphabetically sorted. + * + * @param members Members to be validated. + * + * @return True if all members are correctly sorted. + */ + function checkAlphaSort(members: Member[]): boolean { + let previousName = ''; + let isCorrectlySorted = true; + + // Find first member which isn't correctly sorted + members.forEach(member => { + const name = getMemberName(member, context.getSourceCode()); + + // Note: Not all members have names + if (name) { + if (name < previousName) { + context.report({ + node: member, + messageId: 'incorrectOrder', + data: { + member: name, + beforeMember: previousName, + }, + }); + + isCorrectlySorted = false; + } + + previousName = name; + } + }); + + return isCorrectlySorted; + } + /** * Validates if all members are correctly sorted. * @@ -396,119 +468,39 @@ export default util.createRule({ * @param supportsModifiers A flag indicating whether the type supports modifiers (scope or accessibility) or not. */ function validateMembersOrder( - members: (TSESTree.ClassElement | TSESTree.TypeElement)[], + members: Member[], orderConfig: OrderConfig, supportsModifiers: boolean, ): void { - if (members.length > 0 && orderConfig !== 'never') { + if (orderConfig !== 'never') { + // Standardize config + let order = null; + let memberTypes; + if (Array.isArray(orderConfig)) { - // Sort member groups (= ignore alphabetic order) - const memberGroupsOrder = orderConfig; - - const previousRanks: number[] = []; - - // Find first member which isn't correctly sorted - members.forEach(member => { - const rank = getRank(member, memberGroupsOrder, supportsModifiers); - const name = getMemberName(member, context.getSourceCode()); - - if (rank !== -1) { - // Make sure member types are correctly grouped - // Works for 1st item because x < undefined === false for any x (typeof string) - if (rank < previousRanks[previousRanks.length - 1]) { - context.report({ - node: member, - messageId: 'incorrectGroupOrder', - data: { - name, - rank: getLowestRank(previousRanks, rank, memberGroupsOrder), - }, - }); - } else { - previousRanks.push(rank); - } - } - }); - } else if (orderConfig.memberTypes === 'never') { - // Sort members alphabetically + ignore groups - - let previousName = ''; - - // console.log(members) - - // Find first member which isn't correctly sorted - members.forEach(member => { - const name = getIdentifier(member, context.getSourceCode()); - - // Same member group --> Check alphabetic order - if (name) { - // Not all members have sortable identifiers - if (name < previousName) { - context.report({ - node: member, - messageId: 'incorrectOrder', - data: { - member: name, - beforeMember: previousName, - }, - }); - } - - previousName = name; - } - }); + memberTypes = orderConfig; } else { - // Sort groups + sort alphabetically within each group - const memberGroupsOrder = orderConfig.memberTypes || defaultOrder; - - const previousRanks: number[] = []; - let previousName = ''; - - // Find first member which isn't correctly sorted - members.forEach(member => { - const rank = getRank(member, memberGroupsOrder, supportsModifiers); - const name = getIdentifier(member, context.getSourceCode()); - - if (rank !== -1) { - // Make sure member types are correctly grouped - // Works for 1st item because x < undefined === false for any x (typeof string) - if (rank < previousRanks[previousRanks.length - 1]) { - context.report({ - node: member, - messageId: 'incorrectGroupOrder', - data: { - name: getMemberName(member, context.getSourceCode()), - rank: getLowestRank(previousRanks, rank, memberGroupsOrder), - }, - }); - } else if (rank === previousRanks[previousRanks.length - 1]) { - // Same member group --> Check alphabetic order - if (name) { - // Not all members have sortable identifiers - if (previousName) { - if (name < previousName) { - context.report({ - node: member, - messageId: 'incorrectOrder', - data: { - member: name, - beforeMember: previousName, - }, - }); - } - - previousName = name; - } - } - } else { - previousRanks.push(rank); - - if (name) { - previousName = name; - } - } - } - }); + order = orderConfig.order; + memberTypes = orderConfig.memberTypes; + } + + // Check order + if (Array.isArray(memberTypes)) { + const grouped = checkGroupSort( + members, + memberTypes, + supportsModifiers, + ); + + if (grouped === null) { + return; + } + + if (order === 'alphabetically') { + grouped.some(groupMember => !checkAlphaSort(groupMember)); + } + } else if (order === 'alphabetically') { + checkAlphaSort(members); } } } diff --git a/packages/eslint-plugin/tests/rules/member-ordering.test.ts b/packages/eslint-plugin/tests/rules/member-ordering.test.ts index 4ad4b429153b..f10c3e5c3047 100644 --- a/packages/eslint-plugin/tests/rules/member-ordering.test.ts +++ b/packages/eslint-plugin/tests/rules/member-ordering.test.ts @@ -1,4 +1,8 @@ -import rule, { MessageIds, Options } from '../../src/rules/member-ordering'; +import rule, { + defaultOrder, + MessageIds, + Options, +} from '../../src/rules/member-ordering'; import { RuleTester } from '../RuleTester'; import { TSESLint } from '@typescript-eslint/experimental-utils'; @@ -1433,7 +1437,7 @@ interface Foo { column: 5, }, { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'Z', rank: 'field', @@ -1531,7 +1535,7 @@ interface Foo { column: 5, }, { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'Z', rank: 'field', @@ -1632,7 +1636,7 @@ interface Foo { column: 5, }, { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'Z', rank: 'field', @@ -1833,7 +1837,7 @@ type Foo = { column: 5, }, { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'new', rank: 'field', @@ -2023,7 +2027,7 @@ type Foo = { column: 5, }, { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'Z', rank: 'field', @@ -3339,7 +3343,6 @@ const foo = class { }, ], }, - { code: ` class Foo { @@ -3370,7 +3373,7 @@ class Foo { column: 5, }, { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'Z', rank: 'public instance method', @@ -3518,7 +3521,7 @@ abstract class Foo { `, errors: [ { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'B', rank: 'public abstract field', @@ -3539,7 +3542,7 @@ abstract class Foo { options: [{ default: ['method', 'constructor', 'field'] }], errors: [ { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'C', rank: 'field', @@ -3571,7 +3574,7 @@ class Foo { ], errors: [ { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'D', rank: 'signature', @@ -3587,13 +3590,12 @@ abstract class Foo { abstract B: string; abstract A(): void; public C(): {}; - } `, options: [{ default: ['method', 'constructor', 'field'] }], errors: [ { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'A', rank: 'field', @@ -3602,7 +3604,7 @@ abstract class Foo { column: 5, }, { - messageId: 'incorrectOrder', + messageId: 'incorrectGroupOrder', data: { name: 'C', rank: 'field', @@ -3625,8 +3627,8 @@ const sortedWithoutGroupingDefaultOption: TSESLint.RunTests< code: ` interface Foo { a : b; + [a: string] : number; b() : void; - [a: string] : number; // Will be ignored (no sortable identifier) new () : Bar; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } @@ -3661,8 +3663,8 @@ interface Foo { code: ` type Foo = { a : b; + [a: string] : number; b() : void; - [a: string] : number; // Will be ignored (no sortable identifier) new () : Bar; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } @@ -3775,7 +3777,7 @@ const foo = class Foo { interface Foo { b() : void; a : b; - [a: string] : number; // Will be ignored (no sortable identifier) + [a: string] : number; new () : Bar; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } @@ -3826,7 +3828,7 @@ interface Foo { type Foo = { b() : void; a : b; - [a: string] : number; // Will be ignored (no sortable identifier) + [a: string] : number; new () : Bar; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } @@ -3988,10 +3990,10 @@ const sortedWithoutGroupingClassesOption: TSESLint.RunTests< { code: ` interface Foo { + [a: string] : number; c : b; new () : Bar; b() : void; - [a: string] : number; () : Baz; } `, @@ -4024,10 +4026,10 @@ interface Foo { { code: ` type Foo = { + [a: string] : number; c : b; new () : Bar; b() : void; - [a: string] : number; () : Baz; } `, @@ -4197,10 +4199,10 @@ const sortedWithoutGroupingClassExpressionsOption: TSESLint.RunTests< { code: ` interface Foo { + [a: string] : number; c : b; new () : Bar; b() : void; - [a: string] : number; () : Baz; } `, @@ -4239,10 +4241,10 @@ interface Foo { { code: ` type Foo = { + [a: string] : number; c : b; new () : Bar; b() : void; - [a: string] : number; () : Baz; } `, @@ -4434,9 +4436,9 @@ const sortedWithoutGroupingInterfacesOption: TSESLint.RunTests< { code: ` interface Foo { + [a: string] : number; a : b; b() : void; - [a: string] : number; // Will be ignored (no sortable identifier) new () : Bar; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } @@ -4476,10 +4478,10 @@ interface Foo { { code: ` type Foo = { + [a: string] : number; c : b; new () : Bar; b() : void; - [a: string] : number; () : Baz; } `, @@ -4609,7 +4611,7 @@ const foo = class Foo { interface Foo { b() : void; a : b; - [a: string] : number; // Will be ignored (no sortable identifier) + [a: string] : number; new () : Bar; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } @@ -4669,10 +4671,10 @@ const sortedWithoutGroupingTypeLiteralsOption: TSESLint.RunTests< { code: ` interface Foo { + [a: string] : number; c : b; new () : Bar; b() : void; - [a: string] : number; () : Baz; } `, @@ -4711,9 +4713,9 @@ interface Foo { { code: ` type Foo = { + [a: string] : number; a : b; b() : void; - [a: string] : number; // Will be ignored (no sortable identifier) new () : Bar; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } @@ -4844,7 +4846,7 @@ const foo = class Foo { type Foo = { b() : void; a : b; - [a: string] : number; // Will be ignored (no sortable identifier) + [a: string] : number; new () : Bar; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } @@ -4904,6 +4906,8 @@ const sortedWithGroupingDefaultOption: TSESLint.RunTests< { code: ` interface Foo { + [a: string] : number; + a : x; b : x; c : x; @@ -4914,11 +4918,12 @@ interface Foo { b() : void; c() : void; - [a: string] : number; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], }, // default option + interface + custom order + alphabetically @@ -4935,7 +4940,7 @@ interface Foo { b : x; c : x; - [a: string] : number; // Will be ignored (no sortable identifier) + [a: string] : number; () : Baz; // Will be ignored (no sortable identifier) } `, @@ -4953,6 +4958,8 @@ interface Foo { { code: ` type Foo = { + [a: string] : number; + a : x; b : x; c : x; @@ -4963,17 +4970,20 @@ type Foo = { b() : void; c() : void; - [a: string] : number; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], }, // default option + type literal + custom order + alphabetically { code: ` type Foo = { + [a: string] : number; + new () : Bar; a() : void; @@ -4984,7 +4994,6 @@ type Foo = { b : x; c : x; - [a: string] : number; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) } `, @@ -5013,7 +5022,9 @@ class Foo { constructor() {} } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], }, // default option + class + custom order + alphabetically @@ -5056,7 +5067,9 @@ const foo = class Foo { constructor() {} } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], }, // default option + class expression + custom order + alphabetically @@ -5089,6 +5102,8 @@ const foo = class Foo { { code: ` interface Foo { + [a: string] : number; + a : x; b : x; c : x; @@ -5097,28 +5112,15 @@ interface Foo { b() : void; a() : void; - [a: string] : number; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) - + new () : Bar; } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, { messageId: 'incorrectGroupOrder', data: { @@ -5133,6 +5135,8 @@ interface Foo { { code: ` type Foo = { + [a: string] : number; + a : x; b : x; c : x; @@ -5141,28 +5145,15 @@ type Foo = { b() : void; a() : void; - [a: string] : number; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) - + new () : Bar; } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, { messageId: 'incorrectGroupOrder', data: { @@ -5182,31 +5173,19 @@ class Foo { public static a: string; constructor() {} - + public d: string = ""; } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, { messageId: 'incorrectGroupOrder', data: { name: 'd', - rank: 'constructor', + rank: 'public constructor', }, }, ], @@ -5221,31 +5200,19 @@ const foo = class Foo { public static a: string; constructor() {} - + public d: string = ""; } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, { messageId: 'incorrectGroupOrder', data: { name: 'd', - rank: 'constructor', + rank: 'public constructor', }, }, ], @@ -5262,17 +5229,18 @@ const sortedWithGroupingClassesOption: TSESLint.RunTests< { code: ` interface Foo { + [a: string] : number; + c : x; b : x; a : x; new () : Bar; - + c() : void; b() : void; a() : void; - [a: string] : number; () : Baz; } `, @@ -5283,17 +5251,18 @@ interface Foo { { code: ` type Foo = { + [a: string] : number; + c : x; b : x; a : x; - + new () : Bar; c() : void; b() : void; a() : void; - [a: string] : number; () : Baz; } `, @@ -5315,7 +5284,9 @@ class Foo { constructor() {} } `, - options: [{ classes: { order: 'alphabetically' } }], + options: [ + { classes: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], }, // classes option + class + custom order + alphabetically @@ -5350,11 +5321,11 @@ const foo = class Foo { public static a: string; protected static b: string = ""; private static c: string = ""; - + public d: string = ""; protected e: string = ""; private f: string = ""; - + constructor() {} } `, @@ -5371,31 +5342,19 @@ class Foo { public static a: string; constructor() {} - + public d: string = ""; } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, { messageId: 'incorrectGroupOrder', data: { name: 'd', - rank: 'constructor', + rank: 'public constructor', }, }, ], @@ -5412,17 +5371,18 @@ const sortedWithGroupingClassExpressionsOption: TSESLint.RunTests< { code: ` interface Foo { + [a: string] : number; + c : x; b : x; a : x; - + new () : Bar; c() : void; b() : void; a() : void; - [a: string] : number; () : Baz; } `, @@ -5433,17 +5393,18 @@ interface Foo { { code: ` type Foo = { + [a: string] : number; + c : x; b : x; a : x; new () : Bar; - + c() : void; b() : void; a() : void; - [a: string] : number; () : Baz; } `, @@ -5457,7 +5418,7 @@ class Foo { public static a: string; protected static b: string = ""; private static c: string = ""; - + public d: string = ""; protected e: string = ""; private f: string = ""; @@ -5483,7 +5444,14 @@ const foo = class Foo { constructor() {} } `, - options: [{ classExpressions: { order: 'alphabetically' } }], + options: [ + { + classExpressions: { + memberTypes: defaultOrder, + order: 'alphabetically', + }, + }, + ], }, // classExpressions option + class expression + custom order + alphabetically @@ -5521,31 +5489,19 @@ const foo = class Foo { public static a: string; constructor() {} - + public d: string = ""; } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, { messageId: 'incorrectGroupOrder', data: { name: 'd', - rank: 'constructor', + rank: 'public constructor', }, }, ], @@ -5562,21 +5518,29 @@ const sortedWithGroupingInterfacesOption: TSESLint.RunTests< { code: ` interface Foo { + [a: string] : number; + a : x; b : x; c : x; - new () : Bar; - a() : void; b() : void; c() : void; - [a: string] : number; // Will be ignored (no sortable identifier) + new () : Bar; + () : Baz; // Will be ignored (no sortable identifier) } `, - options: [{ interfaces: { order: 'alphabetically' } }], + options: [ + { + interfaces: { + memberTypes: ['signature', 'field', 'method', 'constructor'], + order: 'alphabetically', + }, + }, + ], }, // interfaces option + interface + custom order + alphabetically @@ -5593,7 +5557,7 @@ interface Foo { b : x; c : x; - [a: string] : number; // Will be ignored (no sortable identifier) + [a: string] : number; () : Baz; // Will be ignored (no sortable identifier) } `, @@ -5611,17 +5575,18 @@ interface Foo { { code: ` type Foo = { + [a: string] : number; + c : x; b : x; a : x; new () : Bar; - + c() : void; b() : void; a() : void; - [a: string] : number; () : Baz; } `, @@ -5669,6 +5634,8 @@ const foo = class Foo { { code: ` interface Foo { + [a: string] : number; + a : x; b : x; c : x; @@ -5677,28 +5644,15 @@ interface Foo { b() : void; a() : void; - [a: string] : number; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) - + new () : Bar; } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, { messageId: 'incorrectGroupOrder', data: { @@ -5720,17 +5674,18 @@ const sortedWithGroupingTypeLiteralsOption: TSESLint.RunTests< { code: ` interface Foo { + [a: string] : number; + c : x; b : x; a : x; new () : Bar; - + c() : void; b() : void; a() : void; - [a: string] : number; () : Baz; } `, @@ -5741,21 +5696,29 @@ interface Foo { { code: ` type Foo = { + [a: string] : number; + a : x; b : x; c : x; - new () : Bar; - a() : void; b() : void; c() : void; - [a: string] : number; + new () : Bar; + () : Baz; } `, - options: [{ typeLiterals: { order: 'alphabetically' } }], + options: [ + { + typeLiterals: { + memberTypes: ['signature', 'field', 'method', 'constructor'], + order: 'alphabetically', + }, + }, + ], }, // typeLiterals option + type literal + custom order + alphabetically @@ -5772,7 +5735,7 @@ type Foo = { b : x; c : x; - [a: string] : number; // Will be ignored (no sortable identifier) + [a: string] : number; () : Baz; // Will be ignored (no sortable identifier) } `, @@ -5793,7 +5756,7 @@ class Foo { public static a: string; protected static b: string = ""; private static c: string = ""; - + public d: string = ""; protected e: string = ""; private f: string = ""; @@ -5811,7 +5774,7 @@ const foo = class Foo { public static a: string; protected static b: string = ""; private static c: string = ""; - + public d: string = ""; protected e: string = ""; private f: string = ""; @@ -5827,6 +5790,8 @@ const foo = class Foo { { code: ` type Foo = { + [a: string] : number; + a : x; b : x; c : x; @@ -5835,28 +5800,15 @@ type Foo = { b() : void; a() : void; - [a: string] : number; // Will be ignored (no sortable identifier) () : Baz; // Will be ignored (no sortable identifier) - + new () : Bar; } `, - options: [{ default: { order: 'alphabetically' } }], + options: [ + { default: { memberTypes: defaultOrder, order: 'alphabetically' } }, + ], errors: [ - { - messageId: 'incorrectOrder', - data: { - member: 'b', - beforeMember: 'c', - }, - }, - { - messageId: 'incorrectOrder', - data: { - member: 'a', - beforeMember: 'b', - }, - }, { messageId: 'incorrectGroupOrder', data: { diff --git a/packages/typescript-estree/src/ts-estree/ts-estree.ts b/packages/typescript-estree/src/ts-estree/ts-estree.ts index 94d4b7c24408..560f46b92fc2 100644 --- a/packages/typescript-estree/src/ts-estree/ts-estree.ts +++ b/packages/typescript-estree/src/ts-estree/ts-estree.ts @@ -308,11 +308,9 @@ export type BindingPattern = ArrayPattern | ObjectPattern; export type BindingName = BindingPattern | Identifier; export type ClassElement = | ClassProperty - | FunctionExpression | MethodDefinition | TSAbstractClassProperty | TSAbstractMethodDefinition - | TSEmptyBodyFunctionExpression | TSIndexSignature; export type ClassProperty = | ClassPropertyComputedName