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

Add ts enums #15

Merged
merged 30 commits into from
Sep 8, 2016
Merged

Add ts enums #15

merged 30 commits into from
Sep 8, 2016

Conversation

jcogilvie
Copy link
Contributor

I added a few things here (with tests):

  1. ability to reference other .json files (locally) with $ref, and choose whether to have the referenced types declared with the existing declareReferenced option.
  2. option to export the generated interfaces
  3. fall back to filename for naming something without ID or title
  4. option to generate ts enums (int-backed instead of string union) and additionally a proprietary schema extension tsEnumNames so that you can specify which integers to use, if you care, like so:
enum: [1, 2, 3]
tsEnumNames: ["one", "two", "three"]

This will generate

enum Whatever {
    one=1,
    two=2,
    three=3
}

Additionally any nested enums will be extracted into top-level types.

// or generate literals for the values

// TODO: what to do in the case where the value is an Object?
// right now we just pring [Object object] as the literal which is bad
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only real concern I have about the code here. I'll call attention to the example of this on the enum test case.

@bcherny
Copy link
Owner

bcherny commented Aug 30, 2016

Thanks for the contribution - will take a look this week.

@@ -0,0 +1,23 @@
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't check this in, I don't use this setup.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setup doesn't use anything environment-specific. It's generalizable so long as we use mocha, so if someone writes TS in vscode, this run configuration can save them the work of figuring out how to debug tests. I think it adds value, but obviously it's your project so I'll remove it if you still think I should.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean. Also I realized that .vscode/settings is already checked in - let's keep this guy here :)

@bcherny
Copy link
Owner

bcherny commented Sep 3, 2016

@jcogilvie Again, thanks for the contribution. At a high level, I have a few concerns:

  1. ability to reference other .json files (locally) with $ref, and choose whether to have the referenced types declared with the existing declareReferenced option.

This is useful, but your implementation does not seem to comply with the official spec. See https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/develop/tests/draft4/ref.json

  1. option to export the generated interfaces

As commented before, this is a great idea, and should be enabled by default. No need to add the ability to disable export unless you have a compelling use case.

  1. fall back to filename for naming something without ID or title

Also a good idea, but see 1.

  1. option to generate ts enums (int-backed instead of string union) and additionally a proprietary schema extension tsEnumNames so that you can specify which integers to use.

Another good idea, but the implementation is overly permissive. Again, see the comments.

If you want, it could be helpful to split 1+3, 2, and 4 into separate PRs.

@bcherny
Copy link
Owner

bcherny commented Sep 3, 2016

Also please rebase off master and run the linter (gulp lint).

@jcogilvie
Copy link
Contributor Author

How do you feel about marking the generated enum util classes as beta or something, maybe by renaming the setting?

I definitely need them as-is for my use case, but I understand that you guys want to discuss them further. I also don't want to spend much longer chasing merges on this PR. It's kind of big.

@jcogilvie
Copy link
Contributor Author

Re: $ref implementation, I haven't implemented the full spec for sure, but the spec does call out support for file:// references which in my mind means having support for relative paths on the filesystem is valid, which is all that my implementation does.

@bcherny
Copy link
Owner

bcherny commented Sep 6, 2016

@jcogilvie
Copy link
Contributor Author

To sum up yesterday's conversation, I agree that string enums should be represented as string unions. Integer enums should use my new code.

If I make that change and kill the util code, will the rest of the PR be accepted as-is?

Jonathan Ogilvie added 2 commits September 7, 2016 11:43
…s for int-backed enums and string unions for string-backed
value: string

constructor(enumValues: string[]) {
let hasValue = !!enumValues[0]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is an enum of ['', 'foo'] valid? Should the check be const hasValue = enumValues.length > 0?

Copy link
Contributor Author

@jcogilvie jcogilvie Sep 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what what would resolve to, symbolically, when trying to reference the member?

These are only used in the case of integer backed enums, and I can't think of why we would assign ''=0.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An enum value of 0 is valid, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah but I'm not sure a symbolic value of "empty string" is something that we want to produce in an enum declaration. It seems ripe for conflation with null and undefined semantics.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine, but this package should support what TS supports. Ts supports enum a {''}, so should we.

If you want to avoid empty string enums, I recommend a tslint rule.

Copy link
Contributor Author

@jcogilvie jcogilvie Sep 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't generate string identifiers. We generate literal identifiers. So this would be a very special case for a not very often used item, and I'm not sure how we would differentiate it... Our output is:

export enum Bar {
  One = 1,
  Two = 2,
  Three = 3
}

Not

export enum Bar {
  "One" = 1,
  "Two" = 2,
  "Three" = 3
}

Really we need to be worried about what the schema supports--and we support strings or integers as enum member values. If they added '' as a value in a string enum, it would work fine (as this EnumValue class is used for int enums only). But as far as our own proprietary extension for the names goes, we can declare whatever requirements we want on those values. An empty string is a special case here (as we don't support any other quoted literal); do you think it's worth the effort?

Although I suppose if you had "''" (a double-quoted set of single-quotes) as the tsEnumName in the schema, that might print an empty string as the identifier...

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's throw an error here, and treat it as tech debt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look at the call site for constructing these. This is a zipped array; assuming we pass all other validation for an int enum, what are the possible outcomes here that we need to handle?

I actually think most of this can probably go away... this was used to propagate string values in enum: [...] up to identifiers for enum declaration, which we no longer do.

var enumValues = zip(rule.tsEnumNames || [],
                // If we try to create a literal from an object, bad stuff can happen... so we have to toString it
                rule.enum!.map(_ => new TsType.Literal(_).toType(this.settings).toString()))
              .map(_ => new TsType.EnumValue(_))

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

@bcherny
Copy link
Owner

bcherny commented Sep 7, 2016

If I make that change and kill the util code, will the rest of the PR be accepted as-is?

I made a last round of comments - it's all minor stuff. The PR looks great @jcogilvie. I look forward to finally getting it merged! In the future, breaking code up into separate PRs by feature could be a good idea :)

@jcogilvie
Copy link
Contributor Author

jcogilvie commented Sep 7, 2016

Ha. Yes, this PR grew since I first opened it. My own use case became clearer and I had to add more stuff. In the future I'll probably keep one mainline branch to house all my aggregate changes with individual branches for each.

@bcherny
Copy link
Owner

bcherny commented Sep 7, 2016

@jcogilvie Ping me here when code is ready to go and I'll merge it.

@jcogilvie
Copy link
Contributor Author

@bcherny I think we're good to go.

@bcherny
Copy link
Owner

bcherny commented Sep 8, 2016

Looks good - thanks @jcogilvie!

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

Successfully merging this pull request may close these issues.

2 participants