bcherny / json-schema-to-typescript

Compile JSON Schema to TypeScript type declarations
https://bcherny.github.io/json-schema-to-typescript-browser/
MIT License
2.95k stars 393 forks source link

Enum generator creates invalid properties when value contain special characters #641

Open quentinverlhac opened 3 weeks ago

quentinverlhac commented 3 weeks ago

Enum generator creates invalid properties when value contain special characters

Description

When generating TypeScript enums from JSON Schema enums that contain special characters in some of their values, the resulting enum properties are invalid TypeScript identifiers.

Steps to Reproduce

  1. Create a JSON Schema example.schema.json with an enum that contains special characters:
{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "user.created",
        "email/verified",
        "2fa-enabled",
        "account*deleted"
      ]
    }
  }
}
  1. Generate TypeScript types using json-schema-to-typescript
yarn run json2ts --inferStringEnumKeysFromValues -i example.schema.json
  1. Observe an error in the output:
SyntaxError: An enum member name must be followed by a ',', '=', or '}'. (14:5)
  12 |
  13 | export const enum Status {
> 14 | user.created = "user.created",
      |     ^
  15 | email/verified = "email/verified",
  16 | 2fa-enabled = "2fa-enabled",
  17 | account*deleted = "account*deleted"

Alternatively:

  1. Generate TypeScript types using json-schema-to-typescript with formatting disabled

    yarn run json2ts --inferStringEnumKeysFromValues --format false -i example.schema.json
  2. Observe invalid TypeScript:

    
    /* eslint-disable */
    /**
    * This file was automatically generated by json-schema-to-typescript.
    * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
    * and run json-schema-to-typescript to regenerate this file.
    */

export interface ExampleSchema { status?: Status

}

export const enum Status { user.created = "user.created", email/verified = "email/verified", 2fa-enabled = "2fa-enabled", accountdeleted = "accountdeleted" }


### Current Behavior

The generator creates enum properties that directly use the special characters, resulting in invalid TypeScript:

```typescript
enum Status {
  user.created = "user.created",
  email/verified = "email/verified",
  2fa-enabled = "2fa-enabled",
  account*deleted = "account*deleted"
}

Expected Behavior

The generator should escape or transform special characters to create valid TypeScript identifiers while preserving the original values as the enum values:

enum Status {
  'user.created' = "user.created",
  'email/verified' = "email/verified",
  '2fa-enabled' = "2fa-enabled",
  'account*deleted' = "account*deleted"
}

Environment

Additional Context

This is particularly important when working with APIs or existing schemas that use dot notation or other special characters in their enum values.

Possible Solution

Consider quoting the generated property names:

  1. Quote keyName in generateStandaloneEnum (replace keyName with '${keyName}')
  2. (Optional): quote only when the property name contains special characters
    • This would make the implementation more complex unnecessarily as quoted properties without special characters are also valid
  3. Add a CLI option to opt-in this behaviour in order to avoid breaking changes in the generated code
utluiz commented 2 days ago

For what's worth, I stumbled on this issue as well. A simple workaround is making a patch adding quotes around enum keys:

diff --git a/node_modules/json-schema-to-typescript/dist/src/generator.js b/node_modules/json-schema-to-typescript/dist/src/generator.js
index [2](https://bitbucket.org/atlassian/shepherd-front-end/pull-requests/1446/diff#Lpatches/json-schema-to-typescript+15.0.2.patchT2)24571d..20ecd87 100644
--- a/node_modules/json-schema-to-typescript/dist/src/generator.js
+++ b/node_modules/json-schema-to-typescript/dist/src/generator.js
@@ -280,7 +280,7 @@ function generateStandaloneEnum(ast, options) {
         (options.enableConstEnums ? 'const ' : '') +
         `enum ${(0, utils_1.toSafeString)(ast.standaloneName)} {` +
         '\n' +
-        ast.params.map(({ ast, keyName }) => keyName + ' = ' + (0, exports.generateType)(ast, options)).join(',\n') +
+        ast.params.map(({ ast, keyName }) => '"' + keyName + '" = ' + (0, exports.generateType)(ast, options)).join(',\n') +
         '\n' +
         '}');
 }