azat-io / eslint-plugin-perfectionist

🦄 ESLint plugin for sorting various data such as objects, imports, types, enums, JSX props, etc.
https://eslint-plugin-perfectionist.azat.io
MIT License
1.62k stars 28 forks source link

feat(sort-interfaces): add `optionalityOrder` option #117

Closed chirokas closed 1 month ago

chirokas commented 2 months ago

Description

This PR adds the optionalityOrder option to the sort-interfaces rule.

This option has three possible values: ignore, optional-first, required-first.

If the required-first option is selected, then before any sorting/grouping configurations, we ensure that the members array has all its required elements first and all its optional elements second. We then perform the remaining sorting/grouping checks on the required subset and the optional subset.

Additional context

// .eslintrc.json
{
  "custom-groups": {
    "callback": "on*"
  },
  "groups": ["unknown", "callback"],
  "optionalityOrder": "required-first"
}

❌ Incorrect

interface ButtonProps {
  backgroundColor?: string
  label: string
  primary?: boolean
  size?: 'large' | 'medium' | 'small'
  onClick?(): void
}

✅ Correct

interface ButtonProps {
  label: string
  backgroundColor?: string
  primary?: boolean
  size?: 'large' | 'medium' | 'small'
  onClick?(): void
}

What is the purpose of this pull request?

Before submitting the PR, please make sure you do the following

azat-io commented 1 month ago

Thanks for the PR. It's a really good and needed feature. What do you think about using groups? Or do you think it would be better to implement a new optionalityOrder?

chirokas commented 1 month ago

Thanks for the PR. It's a really good and needed feature. What do you think about using groups? Or do you think it would be better to implement a new optionalityOrder?

I think it would be better to implement a new optionalityOrder.

Example :

// .eslintrc.json
{
  "custom-groups": {
    "callback": "on*"
  },
  "groups": ["unknown", "optional", "callback"]
}
interface ButtonProps {
  label: string
  backgroundColor?: string
  primary?: boolean
  size?: 'large' | 'medium' | 'small'
  onClick?(): void
}

Using the defineGroup function on the onClick property causes some problems because the onClick property itself is both an optional property and a callback function.

azat-io commented 1 month ago

Sorry for the long reply.

Okay, I agree with you. And what can you say about group priority and optionalityOrder?

For example, these are my settings:

{
  "rules": {
    "perfectionist/sort-interfaces": [
      "error",
      {
        "type": "natural",
        "order": "asc",
        "custom-groups": {
          "name": "name"
        },
        "groups": ["name", "unknown"],
        "optionalityOrder": "required-first"
      }
    ]
  }
}

How would that work here?

interface Props {
  name?: string
  age: number
}
azat-io commented 1 month ago

Another issue is the naming convention of options.

You're using camelCase in optionalityOrder. But we use kebab-case in 'custom-groups'.

chirokas commented 1 month ago

In my plan, it performs the following steps when called:

  1. Perform sorting/grouping on a required subset and an optional subset.
  2. Put all required members first (or all optional members first).

Correct:

interface Props {
  age: number
  name?: string
}
azat-io commented 1 month ago

Okay, thank you!