Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enums and/or Constants #16

Open
willstott101 opened this issue Dec 6, 2023 · 3 comments
Open

Enums and/or Constants #16

willstott101 opened this issue Dec 6, 2023 · 3 comments

Comments

@willstott101
Copy link

I really like the look of typeschema, but the lack of enum support is a little unfortunate for me.

As I see it, a simple constant type which can only be the value of a given constant would enable enums quite easily.

{
  "definitions": {
    "Message": {
      "type": "object",
      "properties": {
        "encoding": {
          "oneOf": [{
            "constant": "BASE64"
          }, {
            "constant": "STRING"
          }]
        },
        "data": {
          "type": "string"
        }
      }
    }
  },
  "$ref": "Message"
}

Are you looking to add limited extensions to typeschema?

@chriskapp
Copy link
Member

Hi @willstott101 sure so in general we are open to extend TypeSchema but it should make sense for the code generation case.

Regarding enums there is also the "enum" Property, which is available at the ScalarType s.
image

you could use this for all scalar types i.e.

{
  "definitions": {
    "Message": {
      "type": "object",
      "properties": {
        "encoding": {
          "type": "string",
          "enum": ["BASE64", "STRING"]
        },
        "data": {
          "type": "string"
        }
      }
    }
  },
  "$ref": "Message"
}

but indeed currently our code generator does not produce actual Enums. There are currently multiple cases i.e. languages like Java or C# provide Enums as a class-like construct like s.
288511910-83bb4ba2-ade0-4bb1-a06e-02e244226809
288512019-508d1930-7350-4992-a93b-2f412247f3eb

in those cases the language automatically selects a fitting value for each case. Then there are languages like PHP or TypeScript which support plain enums like:
288512681-21141fde-f522-4519-80a1-ac76920c81d4
288513081-306ddbfa-7ab1-45ca-bf1a-b0f9391f425f

but also string baked enums, where you can define the actual value like s.
288512877-c89eba59-6258-4eea-bd1c-6a72c281c700
288513157-d6387cca-c0a4-4d6b-bd02-d5986033c257

Then there are also languages like Go which dont have a class-like enum representation instead you need to define a const.


Until know everything is fine but there is one problem which we have not solved yet, if we support enums we also want to use it at our TypeAPI specification, this means that our SDK generator can produce a client like s.

client.getAll(type: MyEnum, startIndex: number, count: number): Todos

If a user now invokes the getAll method and provides the MyEnum type we send internally an HTTP request and the question is how do we serialize the value of the MyEnum type to the HTTP request so that on the server side we understand this value.
This means we cant use Enums where the language automatically selects the value but instead we must define the int value and then we can use the same int on the client and server side. But this means also that we cant use this simple enum property and we need a new way to define the enum key and value so that we always generate int baked enums like s.

image

Currently enum like oneOf is more like a filter to allow only specific values, it is currently not suited to generate enums. I think we need to find a way to define int baked enums in TypeSchema, which could look like s.:

{
  "definitions": {
    "MyEnum": {
      "type": "integer",
      "cases": {
        "None": 0,
        "Unknown": 1,
        "ConnectionLost": 100,
        "OutlierReading": 200
      }
    },
    "Message": {
      "type": "object",
      "properties": {
        "encoding": {
          "$ref": "MyEnum"
        },
        "data": {
          "type": "string"
        }
      }
    }
  },
  "$ref": "Message"
}

So these are my thoughts regarding enums, we can do this but there is internally a little bit more work :) please let me know if there is a general demand in enums then I try to move this forward.


Regarding constants this is also an interesting topic and in general most programming languages support the concept of a const but I would see it more like a property at an struct with a const value i.e.

{
  "definitions": {
    "DateInterval": {
      "type": "object",
      "properties": {
        "month": {
          "type": "integer",
          "const": 12
        }
      }
    }
  }
}

and then we could generate i.e. a java class like

class DateInterval {
    public static final int MONTH = 12;
}

@willstott101
Copy link
Author

Oh great thank you for such a detailed response. I hadn't realized there was so much more information over at TypeHub.

(I was just using ctrl+F on https://typeschema.org/specification)

In my case I am most interested in the enums that are already covered it turns out. And I'd tend to hope to use them in the way you describe for Java/C#/Typescript without the prescribed values.

There are indeed a great many corner cases. I see that ScalarType.enum is currently string | number... I'm not sure I'd understand what to expect from a numerical enum like that in terms of code generation. Your cases suggestion seems a lot more intuitive for those older c-like enums. I do wonder if that could be an extension to the enum attribute.

The meta-definition would then be more like:

{
  "definitions": {
    "TaggedEnum": {
      "type": "object",
      "additionalProperties": {
        "$ref": "ScalarType",
      }
    },
    "SimpleEnum": {
      "type": "array",
      "items": {
        "oneOf": [{
          "type": "string"
        },{
          "type": "number"
        }],
      }
    },
    "Enum": {
      "oneOf": [{
        "$ref": "TaggedEnum"
      },{
        "$ref": "SimpleEnum"
      }],
    },
    "ScalarType": {
      "type": "object",
      "properties": {
        "enum": {
          "$ref": "Enum"
        },
        ...
      }
    }
  },
  "$ref": "ScalarType"
}

Your sketch for const also looks better than mine. Something to note there, is how well having constant fields can help with discriminating between enum/union types. This is also kind of a feature in typescript (though obviously not at runtime for parsing anything).

interface Banana {
    type: "banana";
    peeled: boolean;
}
interface Apple {
    type: "apple";
    hasWorm: boolean;
}
type Fruit = Apple | Banana;

let thing: Fruit = ...;
if (thing.type === "apple") {
    // thing is known to be apple
}

It'd be great for the generated code to be able to use a discriminator field like when generating validation code for instance.


For full transparency, I'm not currently using TypeSchema but I am very interested in what it's trying to achieve. I'm just discussing here really, I don't know if I'll actually be able to find a use for the official tooling at the moment.

@chriskapp
Copy link
Member

Ok, thanks for your feedback, and this could be indeed a way to support both the simple and tagged enum, for code generation we would then only consider the tagged enum. Also regarding using TypeSchema no problem, specification and tooling wise TypeSchema is still in its early days and discussions move the specification forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants