Enforce (and fix!) sorting of object keys, array values, or TS enums/interfaces/types by adding the @sort
annotation where desired.
Most sorting lint rules require you to make an "all or nothing" sorting decision--sort ALL structures across your codebase or sort nothing. Frequently, sorting arrays is incorrect/would change behavior, but even sorting some objects can hurt readability. But of course there are times where sorting is highly desired, like in large constant objects/lists. Thus, being able to selectively "opt-in" to sorting behavior is ideal.
This rule will alarm when any of the following structures are both (1) preceded by the @sort
annotation in a comment, and (2) have unsorted keys/values/elements/etc:
- Object literal
- Array literal
- TS Enum
- TS Interface
- TS Type Literal
- TS Union
Note that sorting will also be enforced on any nested structures. However, this behavior can be configured--see the Options section.
Examples of incorrect code for this rule:
// @sort
const myObj = {
b: 2,
a: 1,
c: 3,
};
// @sort
const myArray = ['b', 'a', 'c'];
// @sort
enum MyEnum {
B = 'b',
A = 'a',
C = 'c',
}
// @sort
type MyUnion = 'b' | 'a' | 'c';
// @sort
interface MyInterface {
b: string;
a: string;
c: string;
}
// @sort
type MyTypeLiteral = {
b: string;
a: string;
c: string;
};
Examples of correct code for this rule:
// @sort
const myObj = {
a: 1,
b: 2,
c: 3,
};
/*
* Works in block comment, too
* @sort
*/
const myArray = ['a', 'b', 'c'];
The default sorting behavior is as follows:
- Ordering: Normalize a structure's "elements" to strings, then compare using String.prototype.localeCompare with the options
{ numeric: true, caseFirst: 'upper' }
- "Deeply" sort all applicable nested structures
- Consider special tokens like
null
/undefined
/false
/true
to be non-special (i.e., null starts with "n", false starts with "f", etc.) - Push unknown/unsortable array values to the end of the list (e.g., an array that contains the element
{some: 'value'}
) - Sorting is not limited to traditional keys and literal values, we can also sort some expression-based elements:
// @sort const myObj1 = { [MyEnum.A]: 1, [MyEnum.B]: 2, [MyEnum.C]: 3, }; // @sort const myArr = [ my.member.expression, `my.template.literal.${a}`, `my.template.literal.${b}`, some.other.member.expression, ]; // @sort type MyUnion = TypeA | TypeB | TypeC;
This rule does NOT accept traditional options via an .eslintrc
file, but every @sort
annotation accepts the following colon-delimited options:
keys
: (default: true) If prescribed andvalues
is omitted, only sort keys, not values. The structures considered to have "keys" are:- Object Literal
- TS Interface
- TS Type Literal
values
: (default: true) If prescribed andkeys
is omitted, only sort values, not keys. The structures considered to have "values" are:- Array Literal
- TS Enum
reverse
: (default: false) If true, reverse the sorting order.none
: (default: false) If true, do not sort this structure. This is used to "skip" the sorting of specific nested structures.shallow
: (default: false) If true, only apply sorting to this structure and not nested structures.
/* OPTIONS EXAMPLE */
// First annotation says: deep sort everything
// @sort
const myObj = {
a: 1,
b: 2,
c: 3,
// Second annotation says: sort keys-only, reversed, but don't apply that sorting any deeper
// @sort:keys:reverse:shallow
nest1: {
nest2: [
// Since previous annotation was shallow, now we're back to the top-level
// annotation, which includes sorting of array values
'apple',
'banana',
'cherry',
],
g: 6,
f: 5,
e: 4,
},
};
If you don't have any structures you want to sort.