-
Notifications
You must be signed in to change notification settings - Fork 44
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
Feature: Support Logical Ordering for Specific String Literal Types #408
Comments
Hi @akibarika This is similar to #148 and #98. I like this feature a lot, but I'm having trouble understanding the real use case: type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; // Logical order [Question] Are there multiple cases where a user would need to define this specific logical order? If not, why not just disable the rule with Edit: This feature makes actually sense in objects. I see 2 challenges to tackle:
Point 1: Defining multiple configurations At first sight, not technically complicated through options. Point 2: Knowing when to use which configuration This is where this gets a big harder. I don't think there is a way to automatically detect the appropriate logic to use based on code context alone. One potential solution is to implement #400 with custom annotation comments. Assuming that a logic configuration named // @perfectionist/sort-objects { configuration: 'sizeLogic' }
const sizeNames = {
xs: 'XS',
s: 'S',
m: 'Medium',
l: 'Larger',
xl: 'XL'
} |
Hi @hugop95 , Thank you for the quick response! I agree with your points, and I also think it’s worth implementing this feature for objects. Here’s a workaround I’ve been using: "rules": {
"perfectionist/sort-objects": [
"warn",
{
"groups": ["xs", "sm", "md", "lg", "xl", "unknown"],
"customGroups": {
"xs": "xs",
"sm": "sm",
"md": "md",
"lg": "lg",
"xl": "xl"
}
}
]
} It’s not a perfect solution and has some limitations, but it does work for now. |
Thinking about it, there is actually a way if we consider that most "logical" groups are a set of specific keys: we can use the fact that users can pass multiple configurations to a single rule. For [
{
groups: ['r', 'g', 'b'],
customGroups: [
{
groupName: 'r',
elementNamePattern: 'r',
},
{
groupName: 'g',
elementNamePattern: 'g',
},
{
groupName: 'b',
elementNamePattern: 'b',
},
],
useConfigurationIf: {
allElementNamesMatchPattern: '^r|g|b$',
},
},
{}, // Fallback configuration with default rule options
], This means that let obj = {
r: string,
g: string,
b: string,
} would be correctly sorted by @OlivierZal What do you think? |
Hi @hugop95, I agree with you that the best place to configure it should be the Here are some considerations though. First, I think that the semantical sorting topic is legitimate: another common use case which comes to my mind is However, I also see possible consistency issues with sorting. e.g. when there are only interface Range {
min: number
max: number
} but what is the expected behavior when it's mixed with other keys, e.g. interface HTMLCustomElement {
id: string // let's say it's configured to be first
a: string
max: string
mid: string
min: string
z: string
other: string // let's say it's configured to be last
} I agree that
That's why I propose a solution which is not limited to [
{
groups: ['rgb'],
customGroups: [
{
groupName: 'rgb',
elementNamePattern: '^r|g|b$',
internalGroupOrder: ['r', 'g', 'b'],
},
],
},
], so that this: [
{
groups: ['id', 'range', 'unknown', 'other'],
customGroups: [
{
groupName: 'id',
elementNamePattern: '^id$',
},
{
groupName: 'range',
elementNamePattern: '^(min|max)',
internalGroupOrder: ['^min', '^max'],
},
{
groupName: 'other',
elementNamePattern: '^other$',
},
],
},
], would result in this: interface HTMLCustomElement {
id: string
minIndoorTemperature: string
minOutdoorTemperature: string
maxIndoorTemperature: string
a: string
z: string
other: string
} and this: [
{
groups: ['id', 'range', 'unknown', 'other'],
customGroups: [
{
groupName: 'id',
elementNamePattern: '^id$',
},
{
groupName: 'range',
elementNamePattern: '^(min|max)',
internalGroupOrder: ['IndoorTemperature$', 'OutdoorTemperature$'],
},
{
groupName: 'other',
elementNamePattern: '^other$',
},
],
},
], in this: interface HTMLCustomElement {
id: string
minIndoorTemperature: string
maxIndoorTemperature: string
minOutdoorTemperature: string
a: string
z: string
other: string
} What do you think @hugop95? |
I like your proposal a lot, I think that the complexity justifies implementing it as another issue (considering that my proposal and yours are independent). You are essentially adding another layer of Your last example could become interface HTMLCustomElement {
id: string
minIndoorTemperature: string
maxIndoorTemperature: string
minOutdoorTemperature: string
a: string
z: string
other: string
} [
{
groups: ['id', 'range', 'unknown', 'other'],
customGroups: [
{
groupName: 'indoor', // Only used within the `range` group
elementNamePattern: 'IndoorTemperature$'',
},
{
groupName: 'outdoor', // Only used within the `range` group
elementNamePattern: 'OutdoorTemperature$'',
},
{
groupName: 'id',
elementNamePattern: '^id$',
},
{
groupName: 'range',
elementNamePattern: '^(min|max)',
groups: ['indoor', 'outdoor'],
},
{
groupName: 'other',
elementNamePattern: '^other$',
},
],
},
], This is a bit related to #68, where users want to colocate things together. |
@hugop95, you got my point, I think we are on the same page 😊 |
At first glance unfortunately, I don't think that's not something we can easily add, this will deserve its own issue 🙂 |
Thank you @hugop95 & @OlivierZal !! I've just updated to the latest version,
It looks like the change hasn’t been included in the latest release yet. |
The feature hasn't been released yet indeed. It will be here in 4.3.0 soon. |
And thank only @hugop95, he did the whole work! |
Thanks for this feature, it looks like exactly what I need! I’m just curious about your release process. Do you have a timeline for when this might be published? At my workplace, we typically publish features right after merging a PR. The thinking is — if time and energy have been spent on building a feature, it makes sense to make it available as soon as possible for everyone to use. It seems to help keep things moving smoothly, but I understand there are different approaches depending on the project. |
@h0adp0re The release process is handled by @azat-io, who can better explain the approach for this project. In general:
|
@hugop95 Wow, great job on the alphabet. And thanks for the outline. I've noticed this feature grouping behavior in other open-source projects before and I have to say I don't quite get it 😄 — what if the feature being waited after takes several weeks more to refine? I am not here to argue or anything, just curious about the rationale behind the indeterminate waiting. |
@h0adp0re Hello! I will publish the release today |
Thank you very much! |
Describe the rule
In some cases, string literal types in TypeScript are better represented in a specific logical order rather than an alphabetical one. For example:
In this case, the progression from '
xs
' (extra small) to 'xl
' (extra large) follows a clear semantic or domain-specific logic. However, the current sorting rules ineslint-plugin-perfectionist
would reorder these alphabetically:This disrupts the intended meaning and reduces the readability of such types, which are often used in design systems, configuration objects, or domain-specific types.
Code example
Design Systems: Breakpoints in CSS frameworks ('
xs
', 'sm
', 'md
', 'lg
', 'xl
').User States: User states in a workflow ('pending', 'active', 'completed', 'archived').
Error Severity Levels: Severity levels ('low', 'medium', 'high', 'critical').
Task Priorities: Task priority levels ('low', 'normal', 'high', 'urgent').
Font size:
thin
,light
,regular
,bold
.Additional comments
No response
Validations
The text was updated successfully, but these errors were encountered: