YousefED / typescript-json-schema

Generate json-schema from your Typescript sources
BSD 3-Clause "New" or "Revised" License
3.08k stars 318 forks source link

Bug: enum with 1 value is not an enum in the JSON schema (evaluates as constant) #577

Open alita-moore opened 8 months ago

alita-moore commented 8 months ago

Description

When you export an enum with one value it maps to a constant in the schema, but I expect it to map to an enum with one element.

Reproduction

see the reproduction in the following codesandbox: https://codesandbox.io/p/sandbox/lucid-kirch-r3rxlg

in short, the input typescript file is

export enum Test {
  apple = "apple",
}

export enum Test2 {
  apple = "apple",
  orange = "orange",
}

And the command to compile into json schema is

npx typescript-json-schema index.ts '*' -o out/generated.json --required --aliasRefs --refs --titles --propOrder --noExtraProps

Expected Results

I expect the results to be an enum with one element

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "definitions": {
        "Test": {
            "enum": [
                "apple"
            ],
            "type": "string"
        },
        "Test2": {
            "enum": [
                "apple",
                "orange"
            ],
            "type": "string"
        }
    }
}

Actual Results

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "definitions": {
        "Test": {
            "const": "apple",
            "type": "string"
        },
        "Test2": {
            "enum": [
                "apple",
                "orange"
            ],
            "type": "string"
        }
    }
}
GeekyEggo commented 8 months ago

If the value can only be a single value, I would expect it to be a const. Is there any advantage to having it as a single-element enum?

alita-moore commented 8 months ago

Using enums in Typescript except a standard string is helpful for many reasons. For example,

1. the ability to add more values in the future without having to change the implementation

type someType = { key: Enum.Apple, value: string }

In the future

type SomeType = { key: Enum.Apple value: string } | { key: Enum.Orange value: number }

Dependents on SomeType would not need to start using an Enum once the new value is added which reduces overhead.

2. the use of enums in a switch that will error if a case is missed.

switch (EnumValue) { case Enum.Apple: Do something default: // @ts-expect-error -- this will error if a case is missed above EnumValue.toString() }

3. The ability to find the usages of the Enum value easily

By right clicking the Enum definition you can find where in the code that value is indexed. This simplifies finding and replacing usages.

4. Python does not support literal constants

I am using this tool as an interim step to then convert to pydantic. Which means that if it's defined as only a cost and not an Enum then I am unable to enforce the Enum value throughout my python code. That's because you can set the type of a variable in python to the Enum concrete class, but you cannot set the type to a string / const.

This is a few examples but there are more. Using an Enum is helpful even if only one value. Specifically in Typescript. But perhaps a more important point is whether or not it's intuitive for this software to change the input type based on the length of the Enum. It seems to be more intuitive that if an Enum is the input then an Enum will be the output, no?