Repository: azat-io/eslint-plugin-perfectionist
Pull request #423 (2024-12-17 13:59:29)

feat: adds `useConfigurationIf` option to `sort-object-types` and `sort-interfaces`
Follow-up of https://github.com/azat-io/eslint-plugin-perfectionist/pull/411.

## Description

This PR adds the following to both `sort-interfaces` and `sort-object-types`:
- Adds context-based configuration handling:
	- `useConfigurationIf.allNamesMatchPattern`.
	- `useConfigurationIf.declarationMatchesPattern`: also allows use to deprecate `ignorePattern`.
- Adds `type: unsorted` handling. 

### What is the purpose of this pull request?

- [x] New Feature



Patch:
diff --git a/docs/content/rules/sort-array-includes.mdx b/docs/content/rules/sort-array-includes.mdx
index 2b3d105..4c1b7b9 100644
--- a/docs/content/rules/sort-array-includes.mdx
+++ b/docs/content/rules/sort-array-includes.mdx
@@ -129,7 +129,7 @@ Specifies the sorting method.
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
 - `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
-- `'unsorted'` — Do not sort items. To be used with the [`useConfigurationIf`](#useConfigurationIf) option.
+- `'unsorted'` — Do not sort items. To be used with the [`useConfigurationIf`](#useconfigurationif) option.
 
 ### order
 
diff --git a/docs/content/rules/sort-interfaces.mdx b/docs/content/rules/sort-interfaces.mdx
index 4947477..ffde4e7 100644
--- a/docs/content/rules/sort-interfaces.mdx
+++ b/docs/content/rules/sort-interfaces.mdx
@@ -143,6 +143,7 @@ Specifies the sorting method.
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
 - `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
+- `'unsorted'` — Do not sort items. To be used with the [`useConfigurationIf`](#useconfigurationif) option.
 
 ### order
 
@@ -191,10 +192,12 @@ Specifies the sorting locales. See [String.prototype.localeCompare() - locales](
 - `string` — A BCP 47 language tag (e.g. `'en'`, `'en-US'`, `'zh-CN'`).
 - `string[]` — An array of BCP 47 language tags.
 
-### ignorePattern
+### [DEPRECATED] ignorePattern
 
 <sub>default: `[]`</sub>
 
+Use the [useConfigurationIf.declarationMatchesPattern](#useconfigurationif) option alongside [type: unsorted](#type) instead.
+
 Allows you to specify names or patterns for interfaces that should be ignored by this rule. This can be useful if you have specific interfaces that you do not want to sort.
 
 You can specify their names or a regexp pattern to ignore, for example: `'^Component.+'` to ignore all interfaces whose names begin with the word “Component”.
@@ -258,6 +261,62 @@ Specifies how optional and required members should be ordered in TypeScript inte
 - `'required-first'` — Put all required members before optional members.
 - `'mixed'` — Do not enforce any specific order based on optionality.
 
+### useConfigurationIf
+
+<sub>
+  type: `{ allNamesMatchPattern?: string; declarationMatchesPattern?: string }`
+</sub>
+<sub>default: `{}`</sub>
+
+Allows you to specify filters to match a particular options configuration for a given interface.
+
+The first matching options configuration will be used. If no configuration matches, the default options configuration will be used.
+
+- `allNamesMatchPattern` — A regexp pattern that all keys must match.
+
+Example configuration:
+```ts
+{
+  'perfectionist/sort-interfaces': [
+    'error',
+    {
+      groups: ['r', 'g', 'b'], // Sort colors types by RGB
+      customGroups: {
+        r: '^r$',
+        g: '^g$',
+        b: '^b$',
+      },
+      useConfigurationIf: {
+        allNamesMatchPattern: '^r|g|b$',
+      },
+    },
+    {
+      type: 'alphabetical' // Fallback configuration
+    }
+  ],
+}
+```
+
+- `declarationMatchesPattern` — A regexp pattern that the interface declaration must match.
+
+Example configuration:
+```ts
+{
+  'perfectionist/sort-interfaces': [
+    'error',
+    {
+      type: 'unsorted', // Do not sort Metadata interfaces
+      useConfigurationIf: {
+        declarationMatchesPattern: '*Metadata$',
+      },
+    },
+    {
+      type: 'alphabetical' // Fallback configuration
+    }
+  ],
+}
+```
+
 ### groups
 
 <sub>
diff --git a/docs/content/rules/sort-object-types.mdx b/docs/content/rules/sort-object-types.mdx
index 4ee7dbc..7c15e63 100644
--- a/docs/content/rules/sort-object-types.mdx
+++ b/docs/content/rules/sort-object-types.mdx
@@ -105,6 +105,7 @@ Specifies the sorting method.
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
 - `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
+- `'unsorted'` — Do not sort items. To be used with the [`useConfigurationIf`](#useconfigurationif) option.
 
 ### order
 
@@ -153,10 +154,12 @@ Specifies the sorting locales. See [String.prototype.localeCompare() - locales](
 - `string` — A BCP 47 language tag (e.g. `'en'`, `'en-US'`, `'zh-CN'`).
 - `string[]` — An array of BCP 47 language tags.
 
-### ignorePattern
+### [DEPRECATED] ignorePattern
 
 <sub>default: `[]`</sub>
 
+Use the [useConfigurationIf.declarationMatchesPattern](#useconfigurationif) option alongside [type: unsorted](#type) instead.
+
 Allows you to specify names or patterns for object types that should be ignored by this rule. This can be useful if you have specific object types that you do not want to sort.
 
 You can specify their names or a regexp pattern to ignore, for example: `'^Component.+'` to ignore all object types whose names begin with the word “Component”.
@@ -223,6 +226,62 @@ Allows you to group type object keys by their kind, determining whether required
 - `required-first` — Group all required values before optional.
 - `optional-first` — Group all optional values before required.
 
+### useConfigurationIf
+
+<sub>
+  type: `{ allNamesMatchPattern?: string; declarationMatchesPattern?: string }`
+</sub>
+<sub>default: `{}`</sub>
+
+Allows you to specify filters to match a particular options configuration for a given object type.
+
+The first matching options configuration will be used. If no configuration matches, the default options configuration will be used.
+
+- `allNamesMatchPattern` — A regexp pattern that all keys must match.
+
+Example configuration:
+```ts
+{
+  'perfectionist/sort-object-types': [
+    'error',
+    {
+      groups: ['r', 'g', 'b'], // Sort colors types by RGB
+      customGroups: {
+        r: '^r$',
+        g: '^g$',
+        b: '^b$',
+      },
+      useConfigurationIf: {
+        allNamesMatchPattern: '^r|g|b$',
+      },
+    },
+    {
+      type: 'alphabetical' // Fallback configuration
+    }
+  ],
+}
+```
+
+- `declarationMatchesPattern` — A regexp pattern that the object type declaration must match.
+
+Example configuration:
+```ts
+{
+  'perfectionist/sort-object-types': [
+    'error',
+    {
+      type: 'unsorted', // Do not sort Metadata types
+      useConfigurationIf: {
+        declarationMatchesPattern: '*Metadata$',
+      },
+    },
+    {
+      type: 'alphabetical' // Fallback configuration
+    }
+  ],
+}
+```
+
 ### groups
 
 <sub>
diff --git a/docs/content/rules/sort-objects.mdx b/docs/content/rules/sort-objects.mdx
index 369d602..e6f6247 100644
--- a/docs/content/rules/sort-objects.mdx
+++ b/docs/content/rules/sort-objects.mdx
@@ -163,7 +163,7 @@ Specifies the sorting method.
 - `'natural'` — Sort items in a [natural](https://github.com/yobacca/natural-orderby) order (e.g., “item2” < “item10”).
 - `'line-length'` — Sort items by the length of the code line (shorter lines first).
 - `'custom'` — Sort items using the alphabet entered in the [`alphabet`](#alphabet) option.
-- `'unsorted'` — Do not sort items. To be used with the [`useConfigurationIf`](#useConfigurationIf) option.
+- `'unsorted'` — Do not sort items. To be used with the [`useConfigurationIf`](#useconfigurationif) option.
 
 ### order
 
diff --git a/rules/sort-interfaces.ts b/rules/sort-interfaces.ts
index 6267fcf..9a33ea4 100644
--- a/rules/sort-interfaces.ts
+++ b/rules/sort-interfaces.ts
@@ -16,6 +16,7 @@ let defaultOptions: Required<Options[0]> = {
   partitionByNewLine: false,
   newlinesBetween: 'ignore',
   specialCharacters: 'keep',
+  useConfigurationIf: {},
   type: 'alphabetical',
   groupKind: 'mixed',
   ignorePattern: [],
@@ -44,7 +45,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
       description: 'Enforce sorted interface properties.',
       recommended: true,
     },
-    schema: [jsonSchema],
+    schema: jsonSchema,
     type: 'suggestion',
     fixable: 'code',
   },
diff --git a/rules/sort-object-types.ts b/rules/sort-object-types.ts
index 18e3506..b62a8a5 100644
--- a/rules/sort-object-types.ts
+++ b/rules/sort-object-types.ts
@@ -1,11 +1,13 @@
 import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema'
 import type { RuleContext } from '@typescript-eslint/utils/ts-eslint'
 import type { TSESTree } from '@typescript-eslint/types'
+import type { TSESLint } from '@typescript-eslint/utils'
 
 import type { Modifier, Selector, Options } from './sort-object-types.types'
 import type { SortingNode } from '../typings'
 
 import {
+  buildUseConfigurationIfJsonSchema,
   buildCustomGroupsArrayJsonSchema,
   partitionByCommentJsonSchema,
   partitionByNewLineJsonSchema,
@@ -23,6 +25,7 @@ import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-new
 import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
 import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
 import { getCustomGroupsCompareOptions } from '../utils/get-custom-groups-compare-options'
+import { getMatchingContextOptions } from '../utils/get-matching-context-options'
 import { generatePredefinedGroups } from '../utils/generate-predefined-groups'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { singleCustomGroupJsonSchema } from './sort-object-types.types'
@@ -70,6 +73,7 @@ let defaultOptions: Required<Options[0]> = {
   partitionByNewLine: false,
   newlinesBetween: 'ignore',
   specialCharacters: 'keep',
+  useConfigurationIf: {},
   type: 'alphabetical',
   groupKind: 'mixed',
   ignorePattern: [],
@@ -82,43 +86,54 @@ let defaultOptions: Required<Options[0]> = {
 }
 
 export let jsonSchema: JSONSchema4 = {
-  properties: {
-    ignorePattern: {
-      description:
-        'Specifies names or patterns for nodes that should be ignored by rule.',
-      items: {
+  items: {
+    properties: {
+      ignorePattern: {
+        description:
+          'Specifies names or patterns for nodes that should be ignored by rule.',
+        items: {
+          type: 'string',
+        },
+        type: 'array',
+      },
+      useConfigurationIf: buildUseConfigurationIfJsonSchema({
+        additionalProperties: {
+          declarationMatchesPattern: {
+            type: 'string',
+          },
+        },
+      }),
+      partitionByComment: {
+        ...partitionByCommentJsonSchema,
+        description:
+          'Allows you to use comments to separate members into logical groups.',
+      },
+      customGroups: {
+        oneOf: [
+          customGroupsJsonSchema,
+          buildCustomGroupsArrayJsonSchema({ singleCustomGroupJsonSchema }),
+        ],
+      },
+      groupKind: {
+        enum: ['mixed', 'required-first', 'optional-first'],
+        description: 'Specifies top-level groups.',
         type: 'string',
       },
-      type: 'array',
-    },
-    partitionByComment: {
-      ...partitionByCommentJsonSchema,
-      description:
-        'Allows you to use comments to separate members into logical groups.',
+      type: buildTypeJsonSchema({ withUnsorted: true }),
+      partitionByNewLine: partitionByNewLineJsonSchema,
+      specialCharacters: specialCharactersJsonSchema,
+      newlinesBetween: newlinesBetweenJsonSchema,
+      ignoreCase: ignoreCaseJsonSchema,
+      alphabet: alphabetJsonSchema,
+      locales: localesJsonSchema,
+      groups: groupsJsonSchema,
+      order: orderJsonSchema,
     },
-    customGroups: {
-      oneOf: [
-        customGroupsJsonSchema,
-        buildCustomGroupsArrayJsonSchema({ singleCustomGroupJsonSchema }),
-      ],
-    },
-    groupKind: {
-      enum: ['mixed', 'required-first', 'optional-first'],
-      description: 'Specifies top-level groups.',
-      type: 'string',
-    },
-    partitionByNewLine: partitionByNewLineJsonSchema,
-    specialCharacters: specialCharactersJsonSchema,
-    newlinesBetween: newlinesBetweenJsonSchema,
-    ignoreCase: ignoreCaseJsonSchema,
-    alphabet: alphabetJsonSchema,
-    type: buildTypeJsonSchema(),
-    locales: localesJsonSchema,
-    groups: groupsJsonSchema,
-    order: orderJsonSchema,
+    additionalProperties: false,
+    type: 'object',
   },
-  additionalProperties: false,
-  type: 'object',
+  uniqueItems: true,
+  type: 'array',
 }
 
 export default createEslintRule<Options, MESSAGE_ID>({
@@ -138,7 +153,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
       description: 'Enforce sorted object types.',
       recommended: true,
     },
-    schema: [jsonSchema],
+    schema: jsonSchema,
     type: 'suggestion',
     fixable: 'code',
   },
@@ -184,7 +199,37 @@ export let sortObjectTypeElements = <MessageIds extends string>({
   }
 
   let settings = getSettings(context.settings)
-  let options = complete(context.options.at(0), settings, defaultOptions)
+  let sourceCode = getSourceCode(context)
+  let matchedContextOptions = getMatchingContextOptions({
+    nodeNames: elements.map(node =>
+      getNodeName({ typeElement: node, sourceCode }),
+    ),
+    contextOptions: context.options,
+  }).find(options => {
+    if (!options.useConfigurationIf?.declarationMatchesPattern) {
+      return true
+    }
+    if (!parentNodeName) {
+      return false
+    }
+    return matches(
+      parentNodeName,
+      options.useConfigurationIf.declarationMatchesPattern,
+    )
+  })
+  let completeOptions = complete(
+    matchedContextOptions,
+    settings,
+    defaultOptions,
+  )
+  let { type } = completeOptions
+  if (type === 'unsorted') {
+    return
+  }
+  let options = {
+    ...completeOptions,
+    type,
+  }
   validateCustomSortConfiguration(options)
   validateGeneratedGroupsConfiguration({
     customGroups: options.customGroups,
@@ -202,7 +247,6 @@ export let sortObjectTypeElements = <MessageIds extends string>({
     return
   }
 
-  let sourceCode = getSourceCode(context)
   let eslintDisabledLines = getEslintDisabledLines({
     ruleName: context.id,
     sourceCode,
@@ -218,44 +262,10 @@ export let sortObjectTypeElements = <MessageIds extends string>({
         return accumulator
       }
 
-      let name: string
       let lastSortingNode = accumulator.at(-1)?.at(-1)
 
       let { setCustomGroups, defineGroup, getGroup } = useGroups(options)
 
-      let formatName = (value: string): string => value.replace(/[,;]$/u, '')
-
-      if (typeElement.type === 'TSPropertySignature') {
-        if (typeElement.key.type === 'Identifier') {
-          ;({ name } = typeElement.key)
-        } else if (typeElement.key.type === 'Literal') {
-          name = `${typeElement.key.value}`
-        } else {
-          let end: number =
-            typeElement.typeAnnotation?.range.at(0) ??
-            typeElement.range.at(1)! - (typeElement.optional ? '?'.length : 0)
-          name = sourceCode.text.slice(typeElement.range.at(0), end)
-        }
-      } else if (typeElement.type === 'TSIndexSignature') {
-        let endIndex: number =
-          typeElement.typeAnnotation?.range.at(0) ?? typeElement.range.at(1)!
-
-        name = formatName(
-          sourceCode.text.slice(typeElement.range.at(0), endIndex),
-        )
-      } else if ('name' in typeElement.key) {
-        // TSMethodSignature
-        ;({ name } = typeElement.key)
-        /* v8 ignore next 8 - Unsure if we can reach it */
-      } else {
-        name = formatName(
-          sourceCode.text.slice(
-            typeElement.range.at(0),
-            typeElement.range.at(1),
-          ),
-        )
-      }
-
       let selectors: Selector[] = []
       let modifiers: Modifier[] = []
 
@@ -295,6 +305,7 @@ export let sortObjectTypeElements = <MessageIds extends string>({
         defineGroup(predefinedGroup)
       }
 
+      let name = getNodeName({ typeElement, sourceCode })
       if (Array.isArray(options.customGroups)) {
         for (let customGroup of options.customGroups) {
           if (
@@ -450,3 +461,44 @@ export let sortObjectTypeElements = <MessageIds extends string>({
     })
   }
 }
+
+let getNodeName = ({
+  typeElement,
+  sourceCode,
+}: {
+  typeElement: TSESTree.TypeElement
+  sourceCode: TSESLint.SourceCode
+}): string => {
+  let name: string
+
+  let formatName = (value: string): string => value.replace(/[,;]$/u, '')
+
+  if (typeElement.type === 'TSPropertySignature') {
+    if (typeElement.key.type === 'Identifier') {
+      ;({ name } = typeElement.key)
+    } else if (typeElement.key.type === 'Literal') {
+      name = `${typeElement.key.value}`
+    } else {
+      let end: number =
+        typeElement.typeAnnotation?.range.at(0) ??
+        typeElement.range.at(1)! - (typeElement.optional ? '?'.length : 0)
+      name = sourceCode.text.slice(typeElement.range.at(0), end)
+    }
+  } else if (typeElement.type === 'TSIndexSignature') {
+    let endIndex: number =
+      typeElement.typeAnnotation?.range.at(0) ?? typeElement.range.at(1)!
+
+    name = formatName(sourceCode.text.slice(typeElement.range.at(0), endIndex))
+  } else if (
+    typeElement.type === 'TSMethodSignature' &&
+    'name' in typeElement.key
+  ) {
+    ;({ name } = typeElement.key)
+    /* v8 ignore next 8 - Unsure if we can reach it */
+  } else {
+    name = formatName(
+      sourceCode.text.slice(typeElement.range.at(0), typeElement.range.at(1)),
+    )
+  }
+  return name
+}
diff --git a/rules/sort-object-types.types.ts b/rules/sort-object-types.types.ts
index 5f26f60..ed61ad0 100644
--- a/rules/sort-object-types.types.ts
+++ b/rules/sort-object-types.types.ts
@@ -8,26 +8,31 @@ import {
   elementNamePatternJsonSchema,
 } from '../utils/common-json-schemas'
 
-export type Options = [
-  Partial<{
-    customGroups: Record<string, string[] | string> | CustomGroup[]
-    type: 'alphabetical' | 'line-length' | 'natural' | 'custom'
-    /**
-     * @deprecated for {@link `groups`}
-     */
-    groupKind: 'required-first' | 'optional-first' | 'mixed'
-    partitionByComment: string[] | boolean | string
-    newlinesBetween: 'ignore' | 'always' | 'never'
-    specialCharacters: 'remove' | 'trim' | 'keep'
-    locales: NonNullable<Intl.LocalesArgument>
-    groups: (Group[] | Group)[]
-    partitionByNewLine: boolean
-    ignorePattern: string[]
-    order: 'desc' | 'asc'
-    ignoreCase: boolean
-    alphabet: string
-  }>,
-]
+export type Options = Partial<{
+  useConfigurationIf: {
+    declarationMatchesPattern?: string
+    allNamesMatchPattern?: string
+  }
+  type: 'alphabetical' | 'line-length' | 'unsorted' | 'natural' | 'custom'
+  customGroups: Record<string, string[] | string> | CustomGroup[]
+  /**
+   * @deprecated for {@link `groups`}
+   */
+  groupKind: 'required-first' | 'optional-first' | 'mixed'
+  partitionByComment: string[] | boolean | string
+  newlinesBetween: 'ignore' | 'always' | 'never'
+  specialCharacters: 'remove' | 'trim' | 'keep'
+  locales: NonNullable<Intl.LocalesArgument>
+  groups: (Group[] | Group)[]
+  partitionByNewLine: boolean
+  /**
+   * @deprecated for {@link `useConfigurationIf.declarationMatchesPattern`}
+   */
+  ignorePattern: string[]
+  order: 'desc' | 'asc'
+  ignoreCase: boolean
+  alphabet: string
+}>[]
 
 export type SingleCustomGroup = (
   | BaseSingleCustomGroup<IndexSignatureSelector>


Test patch:
diff --git a/test/sort-interfaces.test.ts b/test/sort-interfaces.test.ts
index 6acd0ef..34f82a5 100644
--- a/test/sort-interfaces.test.ts
+++ b/test/sort-interfaces.test.ts
@@ -2038,6 +2038,137 @@ describe(ruleName, () => {
         valid: [],
       },
     )
+
+    describe(`${ruleName}(${type}): allows to use 'useConfigurationIf'`, () => {
+      ruleTester.run(
+        `${ruleName}(${type}): allows to use 'allNamesMatchPattern'`,
+        rule,
+        {
+          invalid: [
+            {
+              errors: [
+                {
+                  data: {
+                    rightGroup: 'g',
+                    leftGroup: 'b',
+                    right: 'g',
+                    left: 'b',
+                  },
+                  messageId: 'unexpectedInterfacePropertiesGroupOrder',
+                },
+                {
+                  data: {
+                    rightGroup: 'r',
+                    leftGroup: 'g',
+                    right: 'r',
+                    left: 'g',
+                  },
+                  messageId: 'unexpectedInterfacePropertiesGroupOrder',
+                },
+              ],
+              options: [
+                {
+                  ...options,
+                  useConfigurationIf: {
+                    allNamesMatchPattern: 'foo',
+                  },
+                },
+                {
+                  ...options,
+                  customGroups: {
+                    r: 'r',
+                    g: 'g',
+                    b: 'b',
+                  },
+                  useConfigurationIf: {
+                    allNamesMatchPattern: '^r|g|b$',
+                  },
+                  groups: ['r', 'g', 'b'],
+                },
+              ],
+              output: dedent`
+                interface Interface {
+                  r: string
+                  g: string
+                  b: string
+                }
+              `,
+              code: dedent`
+                interface Interface {
+                  b: string
+                  g: string
+                  r: string
+                }
+              `,
+            },
+          ],
+          valid: [],
+        },
+      )
+
+      describe(`${ruleName}(${type}): allows to use 'declarationMatchesPattern'`, () => {
+        ruleTester.run(
+          `${ruleName}(${type}): detects declaration name by pattern`,
+          rule,
+          {
+            invalid: [
+              {
+                options: [
+                  {
+                    useConfigurationIf: {
+                      declarationMatchesPattern: '^Interface$',
+                    },
+                    type: 'unsorted',
+                  },
+                  options,
+                ],
+                errors: [
+                  {
+                    data: {
+                      right: 'a',
+                      left: 'b',
+                    },
+                    messageId: 'unexpectedInterfacePropertiesOrder',
+                  },
+                ],
+                output: dedent`
+                  interface OtherInterface {
+                    a: string
+                    b: string
+                  }
+                `,
+                code: dedent`
+                  interface OtherInterface {
+                    b: string
+                    a: string
+                  }
+                `,
+              },
+            ],
+            valid: [
+              {
+                options: [
+                  {
+                    useConfigurationIf: {
+                      declarationMatchesPattern: '^Interface$',
+                    },
+                    type: 'unsorted',
+                  },
+                  options,
+                ],
+                code: dedent`
+                  interface Interface {
+                    b: string
+                    c: string
+                    a: string
+                  }
+                `,
+              },
+            ],
+          },
+        )
+      })
+    })
   })
 
   describe(`${ruleName}: sorting by natural order`, () => {
@@ -3899,6 +4030,26 @@ describe(ruleName, () => {
   })
 
   describe(`${ruleName}: misc`, () => {
+    ruleTester.run(`${ruleName}: allows to use "unsorted" as type`, rule, {
+      valid: [
+        {
+          code: dedent`
+            interface Interface {
+              b: string;
+              c: string;
+              a: string;
+            }
+          `,
+          options: [
+            {
+              type: 'unsorted',
+            },
+          ],
+        },
+      ],
+      invalid: [],
+    })
+
     ruleTester.run(
       `${ruleName}: sets alphabetical asc sorting as default`,
       rule,
diff --git a/test/sort-object-types.test.ts b/test/sort-object-types.test.ts
index 52f347e..3f9ded1 100644
--- a/test/sort-object-types.test.ts
+++ b/test/sort-object-types.test.ts
@@ -1885,6 +1885,183 @@ describe(ruleName, () => {
         invalid: [],
       },
     )
+
+    describe(`${ruleName}(${type}): allows to use 'useConfigurationIf'`, () => {
+      ruleTester.run(
+        `${ruleName}(${type}): allows to use 'allNamesMatchPattern'`,
+        rule,
+        {
+          invalid: [
+            {
+              errors: [
+                {
+                  data: {
+                    rightGroup: 'g',
+                    leftGroup: 'b',
+                    right: 'g',
+                    left: 'b',
+                  },
+                  messageId: 'unexpectedObjectTypesGroupOrder',
+                },
+                {
+                  data: {
+                    rightGroup: 'r',
+                    leftGroup: 'g',
+                    right: 'r',
+                    left: 'g',
+                  },
+                  messageId: 'unexpectedObjectTypesGroupOrder',
+                },
+              ],
+              options: [
+                {
+                  ...options,
+                  useConfigurationIf: {
+                    allNamesMatchPattern: 'foo',
+                  },
+                },
+                {
+                  ...options,
+                  customGroups: {
+                    r: 'r',
+                    g: 'g',
+                    b: 'b',
+                  },
+                  useConfigurationIf: {
+                    allNamesMatchPattern: '^r|g|b$',
+                  },
+                  groups: ['r', 'g', 'b'],
+                },
+              ],
+              output: dedent`
+                type Type = {
+                  r: string
+                  g: string
+                  b: string
+                }
+              `,
+              code: dedent`
+                type Type = {
+                  b: string
+                  g: string
+                  r: string
+                }
+              `,
+            },
+          ],
+          valid: [],
+        },
+      )
+
+      describe(`${ruleName}(${type}): allows to use 'declarationMatchesPattern'`, () => {
+        ruleTester.run(
+          `${ruleName}(${type}): detects declaration name by pattern`,
+          rule,
+          {
+            invalid: [
+              {
+                options: [
+                  {
+                    useConfigurationIf: {
+                      declarationMatchesPattern: '^Type$',
+                    },
+                    type: 'unsorted',
+                  },
+                  options,
+                ],
+                errors: [
+                  {
+                    data: {
+                      right: 'a',
+                      left: 'b',
+                    },
+                    messageId: 'unexpectedObjectTypesOrder',
+                  },
+                ],
+                output: dedent`
+                  type OtherType = {
+                    a: string
+                    b: string
+                  }
+                `,
+                code: dedent`
+                  type OtherType = {
+                    b: string
+                    a: string
+                  }
+                `,
+              },
+            ],
+            valid: [
+              {
+                options: [
+                  {
+                    useConfigurationIf: {
+                      declarationMatchesPattern: '^Type$',
+                    },
+                    type: 'unsorted',
+                  },
+                  options,
+                ],
+                code: dedent`
+                  type Type = {
+                    b: string
+                    c: string
+                    a: string
+                  }
+                `,
+              },
+            ],
+          },
+        )
+
+        ruleTester.run(
+          `${ruleName}(${type}): does not match configuration if no declaration name`,
+          rule,
+          {
+            invalid: [
+              {
+                options: [
+                  {
+                    useConfigurationIf: {
+                      declarationMatchesPattern: '^Type$',
+                    },
+                    type: 'unsorted',
+                  },
+                  options,
+                ],
+                errors: [
+                  {
+                    data: {
+                      right: 'b',
+                      left: 'c',
+                    },
+                    messageId: 'unexpectedObjectTypesOrder',
+                  },
+                ],
+                output: dedent`
+                  type Type = {
+                    a: {
+                      b: string
+                      c: string
+                    }
+                  }
+                `,
+                code: dedent`
+                  type Type = {
+                    a: {
+                      c: string
+                      b: string
+                    }
+                  }
+                `,
+              },
+            ],
+            valid: [],
+          },
+        )
+      })
+    })
   })
 
   describe(`${ruleName}: sorting by natural order`, () => {
@@ -3133,6 +3310,26 @@ describe(ruleName, () => {
   })
 
   describe('misc', () => {
+    ruleTester.run(`${ruleName}: allows to use "unsorted" as type`, rule, {
+      valid: [
+        {
+          code: dedent`
+            type Type = {
+              b: string;
+              c: string;
+              a: string;
+            }
+          `,
+          options: [
+            {
+              type: 'unsorted',
+            },
+          ],
+        },
+      ],
+      invalid: [],
+    })
+
     ruleTester.run(`${ruleName}: ignores semi at the end of value`, rule, {
       valid: [
         dedent`