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

Thinking about typing enum values #32

Open
jcward opened this issue Nov 1, 2019 · 4 comments
Open

Thinking about typing enum values #32

jcward opened this issue Nov 1, 2019 · 4 comments

Comments

@jcward
Copy link

jcward commented Nov 1, 2019

I have a first prototype implementation of Enum support (for js-enums-as-arrays) as follows.

Note: these definitions are not sufficient if you wanted to create / instantiate an enum from typescript (e.g. there is no __enum__ parameter), but they're good enough to read the data out of enum values / instances. My purpose is to use a Haxe codebase from TypeScript with usable type definitions - I don't expect TS to generate enum values, but I just want type definitions to use them in a sensible way.

enum Result {
  VALID;
  INVALID(msg:String);
  PENDING(tbd:Promise<Result>)
}

Which (would) generate as follows:

export type Result =
		[ 'VALID', number ] |
		[ 'INVALID', number, /* msg */string ] |
		[ 'PENDING', number, /* tbd */Promise<Result> ]

As you can see (while not entirely pretty), this accurately describes the enum values, and it's sufficient for TS to determine the types of the arguments in a switch-like way, by identifying the first parameter:

image

However, this is not ready to be merged for three reasons.

First, the above generates the following TypeScript error:

Type alias 'Result' circularly references itself.ts(2456)

Checking out the TS repo, I found an issue and a PR for recursive type definitions, but that's very recent. So I had to put in a hack (specifically to support Promise<T> -- you could also support Array<T>, but not T itself.)

Second, my enum declarations are generating in the .d.ts file. While the abstract enums being generated are being put in the enum.ts companion file. In my project, there are both kinds of enums in the same packages, which puts some namespace pkg declarations in the .d.ts file, and some namespace pkg declarations in the .ts file. So then the .d.ts file tries to import { pkg } from enums.ts - the imported namespace collides with the local namespace.

To get around this, I munge both output files into a single .ts file, and change all my export class to export declare class. But there may be a better way...

Third, we should probably detect if the user used Haxe's -D js-enums-as-arrays or not. The above is for the array syntax (which is what my project uses.) But I wonder if it's possible to support the object syntax... I'll think about it.

@jcward
Copy link
Author

jcward commented Nov 2, 2019

Ah, just realized I should be specifying that second parameter, the number, as a literal also (which Haxe generates as the "position of the constructor"):

export type Result =
		[ 'VALID', 0] |
		[ 'INVALID', 1, /* msg */string ] |
		[ 'PENDING', 2, /* tbd */Promise<Result> ]

See it on the TypeScript playground. The recursive type is working in ts v3.7-beta.

@jcward
Copy link
Author

jcward commented Nov 2, 2019

A possible "enums as objects" syntax is a little more bulky.

@jcward jcward changed the title Thinking about enum support Thinking about typing enum values Nov 2, 2019
@benmerckx
Copy link

Just a little note that in Typescript you can use types and values of the same name in declarations. This can help generate "values" for the constructors too so they can be used from TS. That is if Haxe can actually @:expose/export the enum - for which I think support was lacking. I realized that after typing this out so maybe this is useless :) In genes I generate the declaration of the example enum to:

export declare namespace Result {
  export type VALID = {_hx_index: 0, __enum__: "tests.Result"}
  export const VALID: VALID
  export type PENDING = {_hx_index: 2, tbd: Promise<Result>, __enum__: "tests.Result"}
  export const PENDING: (tbd: Promise<Result>) => Result
  export type INVALID = {_hx_index: 1, msg: string, __enum__: "tests.Result"}
  export const INVALID: (msg: string) => Result
}

export declare type Result = 
  | Result.VALID
  | Result.PENDING
  | Result.INVALID

This doesn't include the separate [EnumName]Constructor type. But you could generate it as well and have full support for enums (ie. we can actually construct the enum values properly): example

Although I'm hoping Haxe would deliver a nicer name than _hx_index :) HaxeFoundation/haxe#8591

@elsassph
Copy link
Owner

elsassph commented Nov 2, 2019

Very (too? 😁) clever, @benmerckx.

Switching in the enum still looks really awkward though... Maybe you it could be solved using a TypeScript transform.

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

3 participants