Repository: azat-io/eslint-plugin-perfectionist
Pull request #532 (2025-04-26 14:58:28)

feat: strengthen JSON schemas
### Description

This PR contains a mix of various JSON schema related fixes and improvements:

- Fix invalid `fallbackSort` JSON schema (it is passing today due to weak validation: missing `additionalProperties: false`)
- Add a test to check for missing `additionalProperties: false` (which generates `[key: string]: uknown` in typings).
- Improve JSON schema descriptions to match the documentation better.

### What is the purpose of this pull request?

- [x] Other



codecov[bot]: ## [Codecov](https://app.codecov.io/gh/azat-io/eslint-plugin-perfectionist/pull/532?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azat+S.) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 100.00%. Comparing base [(`45306fa`)](https://app.codecov.io/gh/azat-io/eslint-plugin-perfectionist/commit/45306fa67b59bb85bb1cf0fd3d53dca7b3ca5de8?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azat+S.) to head [(`15f68ef`)](https://app.codecov.io/gh/azat-io/eslint-plugin-perfectionist/commit/15f68ef0c7df60f608749a7dbd11e4ce10c45bc5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azat+S.).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##              main      #532   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           99        99           
  Lines         8012      8022   +10     
  Branches      1632      1632           
=========================================
+ Hits          8012      8022   +10     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/azat-io/eslint-plugin-perfectionist/pull/532?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azat+S.).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Azat+S.).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>

Patch:
diff --git a/rules/sort-decorators.ts b/rules/sort-decorators.ts
index c761fb9..3149b99 100644
--- a/rules/sort-decorators.ts
+++ b/rules/sort-decorators.ts
@@ -10,13 +10,13 @@ import type {
 import type { SortingNode } from '../types/sorting-node'
 
 import {
+  deprecatedCustomGroupsJsonSchema,
   partitionByCommentJsonSchema,
-  customGroupsJsonSchema,
   commonJsonSchemas,
   groupsJsonSchema,
 } from '../utils/common-json-schemas'
+import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
 import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
-import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
 import { GROUP_ORDER_ERROR, ORDER_ERROR } from '../utils/report-errors'
@@ -77,9 +77,9 @@ export default createEslintRule<Options, MESSAGE_ID>({
 
     let options = complete(context.options.at(0), settings, defaultOptions)
     validateCustomSortConfiguration(options)
-    validateGroupsConfiguration({
-      allowedCustomGroups: Object.keys(options.customGroups),
-      allowedPredefinedGroups: ['unknown'],
+    validateGeneratedGroupsConfiguration({
+      selectors: [],
+      modifiers: [],
       options,
     })
 
@@ -161,7 +161,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
             type: 'boolean',
           },
           partitionByComment: partitionByCommentJsonSchema,
-          customGroups: customGroupsJsonSchema,
+          customGroups: deprecatedCustomGroupsJsonSchema,
           groups: groupsJsonSchema,
         },
         additionalProperties: false,
diff --git a/rules/sort-enums.ts b/rules/sort-enums.ts
index 956a9c1..32ffce6 100644
--- a/rules/sort-enums.ts
+++ b/rules/sort-enums.ts
@@ -7,10 +7,10 @@ import type { Options } from './sort-enums/types'
 
 import {
   buildCustomGroupsArrayJsonSchema,
+  deprecatedCustomGroupsJsonSchema,
   partitionByCommentJsonSchema,
   partitionByNewLineJsonSchema,
   newlinesBetweenJsonSchema,
-  customGroupsJsonSchema,
   commonJsonSchemas,
   groupsJsonSchema,
 } from '../utils/common-json-schemas'
@@ -260,17 +260,17 @@ export default createEslintRule<Options, MESSAGE_ID>({
       {
         properties: {
           ...commonJsonSchemas,
-          forceNumericSort: {
-            description:
-              'Will always sort numeric enums by their value regardless of the sort type specified.',
-            type: 'boolean',
-          },
           customGroups: {
             oneOf: [
-              customGroupsJsonSchema,
+              deprecatedCustomGroupsJsonSchema,
               buildCustomGroupsArrayJsonSchema({ singleCustomGroupJsonSchema }),
             ],
           },
+          forceNumericSort: {
+            description:
+              'Will always sort numeric enums by their value regardless of the sort type specified.',
+            type: 'boolean',
+          },
           sortByValue: {
             description: 'Compare enum values instead of names.',
             type: 'boolean',
diff --git a/rules/sort-heritage-clauses.ts b/rules/sort-heritage-clauses.ts
index da26ab1..d4deb36 100644
--- a/rules/sort-heritage-clauses.ts
+++ b/rules/sort-heritage-clauses.ts
@@ -9,12 +9,12 @@ import type {
 import type { SortingNode } from '../types/sorting-node'
 
 import {
-  customGroupsJsonSchema,
+  deprecatedCustomGroupsJsonSchema,
   commonJsonSchemas,
   groupsJsonSchema,
 } from '../utils/common-json-schemas'
+import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
 import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
-import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
 import { GROUP_ORDER_ERROR, ORDER_ERROR } from '../utils/report-errors'
@@ -60,7 +60,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
       {
         properties: {
           ...commonJsonSchemas,
-          customGroups: customGroupsJsonSchema,
+          customGroups: deprecatedCustomGroupsJsonSchema,
           groups: groupsJsonSchema,
         },
         additionalProperties: false,
@@ -84,9 +84,9 @@ export default createEslintRule<Options, MESSAGE_ID>({
 
     let options = complete(context.options.at(0), settings, defaultOptions)
     validateCustomSortConfiguration(options)
-    validateGroupsConfiguration({
-      allowedCustomGroups: Object.keys(options.customGroups),
-      allowedPredefinedGroups: ['unknown'],
+    validateGeneratedGroupsConfiguration({
+      selectors: [],
+      modifiers: [],
       options,
     })
 
diff --git a/rules/sort-imports.ts b/rules/sort-imports.ts
index 74eb472..de89d84 100644
--- a/rules/sort-imports.ts
+++ b/rules/sort-imports.ts
@@ -15,6 +15,7 @@ import type {
 
 import {
   buildCustomGroupsArrayJsonSchema,
+  deprecatedCustomGroupsJsonSchema,
   partitionByCommentJsonSchema,
   partitionByNewLineJsonSchema,
   newlinesBetweenJsonSchema,
@@ -423,12 +424,12 @@ export default createEslintRule<Options, MESSAGE_ID>({
               {
                 properties: {
                   value: {
+                    ...deprecatedCustomGroupsJsonSchema,
                     description: 'Specifies custom groups for value imports.',
-                    type: 'object',
                   },
                   type: {
+                    ...deprecatedCustomGroupsJsonSchema,
                     description: 'Specifies custom groups for type imports.',
-                    type: 'object',
                   },
                 },
                 description: 'Specifies custom groups.',
diff --git a/rules/sort-jsx-props.ts b/rules/sort-jsx-props.ts
index 68e410f..60e9d79 100644
--- a/rules/sort-jsx-props.ts
+++ b/rules/sort-jsx-props.ts
@@ -7,9 +7,9 @@ import type { Options } from './sort-jsx-props/types'
 import {
   buildUseConfigurationIfJsonSchema,
   buildCustomGroupsArrayJsonSchema,
+  deprecatedCustomGroupsJsonSchema,
   partitionByNewLineJsonSchema,
   newlinesBetweenJsonSchema,
-  customGroupsJsonSchema,
   commonJsonSchemas,
   groupsJsonSchema,
   regexJsonSchema,
@@ -229,7 +229,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           ...commonJsonSchemas,
           customGroups: {
             oneOf: [
-              customGroupsJsonSchema,
+              deprecatedCustomGroupsJsonSchema,
               buildCustomGroupsArrayJsonSchema({
                 singleCustomGroupJsonSchema,
               }),
diff --git a/rules/sort-object-types.ts b/rules/sort-object-types.ts
index bfb3d03..2a4f44d 100644
--- a/rules/sort-object-types.ts
+++ b/rules/sort-object-types.ts
@@ -13,10 +13,10 @@ import type {
 import {
   buildUseConfigurationIfJsonSchema,
   buildCustomGroupsArrayJsonSchema,
+  deprecatedCustomGroupsJsonSchema,
   partitionByCommentJsonSchema,
   partitionByNewLineJsonSchema,
   newlinesBetweenJsonSchema,
-  customGroupsJsonSchema,
   buildCommonJsonSchemas,
   groupsJsonSchema,
   regexJsonSchema,
@@ -95,7 +95,7 @@ export let jsonSchema: JSONSchema4 = {
       }),
       customGroups: {
         oneOf: [
-          customGroupsJsonSchema,
+          deprecatedCustomGroupsJsonSchema,
           buildCustomGroupsArrayJsonSchema({
             additionalFallbackSortProperties: { sortBy: sortByJsonSchema },
             singleCustomGroupJsonSchema,
diff --git a/rules/sort-objects.ts b/rules/sort-objects.ts
index 221e141..c4f95c0 100644
--- a/rules/sort-objects.ts
+++ b/rules/sort-objects.ts
@@ -9,10 +9,10 @@ import type { Modifier, Selector, Options } from './sort-objects/types'
 import {
   buildUseConfigurationIfJsonSchema,
   buildCustomGroupsArrayJsonSchema,
+  deprecatedCustomGroupsJsonSchema,
   partitionByCommentJsonSchema,
   partitionByNewLineJsonSchema,
   newlinesBetweenJsonSchema,
-  customGroupsJsonSchema,
   commonJsonSchemas,
   groupsJsonSchema,
   regexJsonSchema,
@@ -447,7 +447,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
           },
           customGroups: {
             oneOf: [
-              customGroupsJsonSchema,
+              deprecatedCustomGroupsJsonSchema,
               buildCustomGroupsArrayJsonSchema({ singleCustomGroupJsonSchema }),
             ],
           },
diff --git a/rules/sort-union-types.ts b/rules/sort-union-types.ts
index 7c9d8cc..7401c85 100644
--- a/rules/sort-union-types.ts
+++ b/rules/sort-union-types.ts
@@ -24,8 +24,8 @@ import {
   ORDER_ERROR,
 } from '../utils/report-errors'
 import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration'
+import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
 import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration'
-import { validateGroupsConfiguration } from '../utils/validate-groups-configuration'
 import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
 import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
 import { sortNodesByGroups } from '../utils/sort-nodes-by-groups'
@@ -151,8 +151,8 @@ export let sortUnionOrIntersectionTypes = <MessageIds extends string>({
 
   let options = complete(context.options.at(0), settings, defaultOptions)
   validateCustomSortConfiguration(options)
-  validateGroupsConfiguration({
-    allowedPredefinedGroups: [
+  validateGeneratedGroupsConfiguration({
+    selectors: [
       'intersection',
       'conditional',
       'function',
@@ -160,15 +160,17 @@ export let sortUnionOrIntersectionTypes = <MessageIds extends string>({
       'keyword',
       'literal',
       'nullish',
-      'unknown',
       'import',
       'object',
       'named',
       'tuple',
       'union',
     ],
-    allowedCustomGroups: [],
-    options,
+    options: {
+      ...options,
+      customGroups: [],
+    },
+    modifiers: [],
   })
   validateNewlinesAndPartitionConfiguration(options)
 
diff --git a/utils/common-json-schemas.ts b/utils/common-json-schemas.ts
index f589256..fa2967d 100644
--- a/utils/common-json-schemas.ts
+++ b/utils/common-json-schemas.ts
@@ -8,13 +8,14 @@ let typeJsonSchema: JSONSchema4 = {
 
 let orderJsonSchema: JSONSchema4 = {
   description:
-    'Determines whether the sorted items should be in ascending or descending order.',
+    'Specifies whether to sort items in ascending or descending order.',
   enum: ['asc', 'desc'],
   type: 'string',
 }
 
 let alphabetJsonSchema: JSONSchema4 = {
-  description: 'Alphabet to use for the `custom` sort type.',
+  description:
+    "Used only when the `type` option is set to `'custom'`. Specifies the custom alphabet for sorting.",
   type: 'string',
 }
 
@@ -40,7 +41,7 @@ let ignoreCaseJsonSchema: JSONSchema4 = {
 
 let specialCharactersJsonSchema: JSONSchema4 = {
   description:
-    'Controls how special characters should be handled before sorting.',
+    'Specifies whether to trim, remove, or keep special characters before sorting.',
   enum: ['remove', 'trim', 'keep'],
   type: 'string',
 }
@@ -56,6 +57,7 @@ let buildFallbackSortJsonSchema = ({
     ...additionalProperties,
   },
   description: 'Fallback sort order.',
+  additionalProperties: false,
   type: 'object',
 })
 
@@ -64,7 +66,9 @@ export let buildCommonJsonSchemas = ({
 }: {
   additionalFallbackSortProperties?: Record<string, JSONSchema4>
 } = {}): Record<string, JSONSchema4> => ({
-  fallbackSort: buildFallbackSortJsonSchema(additionalFallbackSortProperties),
+  fallbackSort: buildFallbackSortJsonSchema({
+    additionalProperties: additionalFallbackSortProperties,
+  }),
   specialCharacters: specialCharactersJsonSchema,
   ignoreCase: ignoreCaseJsonSchema,
   alphabet: alphabetJsonSchema,
@@ -77,7 +81,7 @@ export let commonJsonSchemas: Record<string, JSONSchema4> =
   buildCommonJsonSchemas()
 
 export let newlinesBetweenJsonSchema: JSONSchema4 = {
-  description: 'Specifies how new lines should be handled between groups.',
+  description: 'Specifies how to handle new lines between groups.',
   enum: ['ignore', 'always', 'never'],
   type: 'string',
 }
@@ -103,11 +107,11 @@ export let groupsJsonSchema: JSONSchema4 = {
       },
     ],
   },
-  description: 'Specifies the order of the groups.',
+  description: 'Specifies a list of groups for sorting.',
   type: 'array',
 }
 
-export let customGroupsJsonSchema: JSONSchema4 = {
+export let deprecatedCustomGroupsJsonSchema: JSONSchema4 = {
   additionalProperties: {
     oneOf: [
       {
@@ -130,9 +134,11 @@ let singleRegexJsonSchema: JSONSchema4 = {
     {
       properties: {
         pattern: {
+          description: 'Regular expression pattern.',
           type: 'string',
         },
         flags: {
+          description: 'Regular expression flags.',
           type: 'string',
         },
       },
@@ -172,9 +178,11 @@ export let partitionByCommentJsonSchema: JSONSchema4 = {
     {
       properties: {
         block: {
+          description: 'Enables specific block comments to separate the nodes.',
           oneOf: allowedPartitionByCommentJsonSchemas,
         },
         line: {
+          description: 'Enables specific line comments to separate the nodes.',
           oneOf: allowedPartitionByCommentJsonSchemas,
         },
       },
@@ -183,12 +191,12 @@ export let partitionByCommentJsonSchema: JSONSchema4 = {
     },
   ],
   description:
-    'Allows to use comments to separate members into logical groups.',
+    'Enables the use of comments to separate the nodes into logical groups.',
 }
 
 export let partitionByNewLineJsonSchema: JSONSchema4 = {
   description:
-    'Allows to use newlines to separate the nodes into logical groups.',
+    'Enables the use of newlines to separate the nodes into logical groups.',
   type: 'boolean',
 }
 
@@ -197,6 +205,8 @@ export let buildUseConfigurationIfJsonSchema = ({
 }: {
   additionalProperties?: Record<string, JSONSchema4>
 } = {}): JSONSchema4 => ({
+  description:
+    'Specifies filters to match a particular options configuration for a given element to sort.',
   properties: {
     allNamesMatchPattern: regexJsonSchema,
     ...additionalProperties,
@@ -212,7 +222,7 @@ let buildCommonCustomGroupJsonSchemas = ({
 } = {}): Record<string, JSONSchema4> => ({
   newlinesInside: {
     description:
-      'Specifies how new lines should be handled between members of the custom group.',
+      'Specifies how to handle new lines between members of the custom group.',
     enum: ['always', 'never'],
     type: 'string',
   },
@@ -270,7 +280,7 @@ export let buildCustomGroupsArrayJsonSchema = ({
       },
     ],
   },
-  description: 'Specifies custom groups.',
+  description: 'Defines custom groups to match specific members.',
   type: 'array',
 })
 
diff --git a/utils/validate-groups-configuration.ts b/utils/validate-groups-configuration.ts
deleted file mode 100644
index 81f10be..0000000
--- a/utils/validate-groups-configuration.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import type { GroupsOptions } from '../types/common-options'
-
-import { validateNewlinesBetweenInsideGroups } from './validate-newlines-between-inside-groups'
-import { validateNoDuplicatedGroups } from './validate-no-duplicated-groups'
-import { isNewlinesBetweenOption } from './is-newlines-between-option'
-
-interface ValidateGroupsConfigurationParameters {
-  options: {
-    groups: GroupsOptions<string>
-  }
-  allowedPredefinedGroups: string[]
-  allowedCustomGroups: string[]
-}
-
-/**
- * Throws an error if one of the following conditions is met:
- * - One or more groups specified in `groups` are not predefined nor specified
- * in `customGroups`
- * - A group is specified in `groups` more than once
- * @param {object} parameters - Parameters object.
- * @param {object} parameters.options - Options containing the groups to validate.
- * @param {string[]} parameters.allowedPredefinedGroups - An array of predefined
- * group names that are considered valid.
- * @param {string[]} parameters.allowedCustomGroups - An array of custom group
- * names that are considered valid.
- * @throws Error Will throw an error if invalid or duplicated groups are found.
- */
-export let validateGroupsConfiguration = ({
-  allowedPredefinedGroups,
-  allowedCustomGroups,
-  options,
-}: ValidateGroupsConfigurationParameters): void => {
-  let allowedGroupsSet = new Set([
-    ...allowedPredefinedGroups,
-    ...allowedCustomGroups,
-  ])
-  let invalidGroups: string[] = []
-
-  for (let groupElement of options.groups) {
-    if (isNewlinesBetweenOption(groupElement)) {
-      continue
-    }
-    let groupElements = Array.isArray(groupElement)
-      ? groupElement
-      : [groupElement]
-    for (let group of groupElements) {
-      if (!allowedGroupsSet.has(group)) {
-        invalidGroups.push(group)
-      }
-    }
-  }
-  if (invalidGroups.length > 0) {
-    throw new Error(`Invalid group(s): ${invalidGroups.join(', ')}`)
-  }
-
-  validateNoDuplicatedGroups(options)
-  validateNewlinesBetweenInsideGroups(options)
-}


Test patch:
diff --git a/test/utils/validate-groups-configuration.test.ts b/test/utils/validate-groups-configuration.test.ts
deleted file mode 100644
index dfa1808..0000000
--- a/test/utils/validate-groups-configuration.test.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { describe, expect, it } from 'vitest'
-
-import { validateGroupsConfiguration } from '../../utils/validate-groups-configuration'
-
-/**
- * It is currently not possible to test rules that throw errors
- * (https://github.com/eslint/eslint/issues/13434), so getting 100% code
- * coverage is not possible only through ESLint's  RuleTester as there is no way
- * to catch the error thrown from `validateGroupsConfiguration`. We can get 100%
- * coverage temporarily with this unit test until that feature is implemented in
- * ESLint.
- *
- */
-describe('validate-groups-configuration', () => {
-  it('throws an error when an invalid group is provided', () => {
-    expect(() => {
-      validateGroupsConfiguration({
-        options: {
-          groups: [
-            'predefinedGroup',
-            ['customGroup', 'invalidGroup1'],
-            'invalidGroup2',
-          ],
-        },
-        allowedPredefinedGroups: ['predefinedGroup'],
-        allowedCustomGroups: ['customGroup'],
-      })
-    }).toThrow('Invalid group(s): invalidGroup1, invalidGroup2')
-  })
-
-  it('throws an error when a duplicate group is provided', () => {
-    expect(() => {
-      validateGroupsConfiguration({
-        options: {
-          groups: ['predefinedGroup', 'predefinedGroup'],
-        },
-        allowedPredefinedGroups: ['predefinedGroup'],
-        allowedCustomGroups: [],
-      })
-    }).toThrow('Duplicated group(s): predefinedGroup')
-  })
-
-  it('throws an error with consecutive newlines objects', () => {
-    expect(() => {
-      validateGroupsConfiguration({
-        options: {
-          groups: [
-            'a',
-            { newlinesBetween: 'always' },
-            { newlinesBetween: 'always' },
-            'b',
-          ],
-        },
-        allowedPredefinedGroups: ['a', 'b'],
-        allowedCustomGroups: [],
-      })
-    }).toThrow("Consecutive 'newlinesBetween' objects are not allowed")
-  })
-})
diff --git a/test/utils/validate-rule-json-schema.ts b/test/utils/validate-rule-json-schema.ts
index fcb51d3..0d3c08c 100644
--- a/test/utils/validate-rule-json-schema.ts
+++ b/test/utils/validate-rule-json-schema.ts
@@ -25,6 +25,12 @@ let validateJsonSchema = async (schema: JSONSchema4): Promise<void> => {
 
 let validateTsJsonSchema = async (schema: JSONSchema4): Promise<void> => {
   let generatedTypescript = await compileSchemaForTs(schema, 'id')
+
+  assertGeneratedTsSeemsCorrect(generatedTypescript)
+  assertNoWeakIndexSignature(generatedTypescript)
+}
+
+let assertGeneratedTsSeemsCorrect = (generatedTypescript: string): void => {
   for (let [commonJsonSchemaKey, commonJsonSchema] of Object.entries(
     commonJsonSchemas,
   )) {
@@ -38,3 +44,11 @@ let validateTsJsonSchema = async (schema: JSONSchema4): Promise<void> => {
     }
   }
 }
+
+let assertNoWeakIndexSignature = (generatedTypescript: string): void => {
+  if (generatedTypescript.includes('[k: string]: unknown')) {
+    throw new Error(
+      `Weak TypeScript generated from JSON schema: '[k: string]: unknown' index signature found:\n${generatedTypescript}`,
+    )
+  }
+}