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

Use Typescript Branded types pattern for better enums implementation #82

Open
vegegoku opened this issue Oct 18, 2023 · 2 comments
Open
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@vegegoku
Copy link
Member

vegegoku commented Oct 18, 2023

In the current implementation of enums we had to introduce some limitations and workarounds, the first one is that instead of using actual enum classes we use normal classes with static final members, then we introduced two annotations TsTypeDef and TsTypeRef and we use those to create type Aliases and assume that all the static final members will exported with the defined type alias, example :

Simulate an enum with constant values :

@JsType
@TsTypeDef(tsType = "string")
public class EnumSimulation {
  public static final String VALUE_1 = "value1";
  public static final String VALUE_2 = "value2";
  public static final String VALUE_3 = "value3";

  private static final String[] VALUES = JsObject.freeze(new String[] {VALUE_1, VALUE_2, VALUE_3});

  @JsMethod
  public String[] values() {
    return VALUES;
  }
}

Which will be exported as

	type EnumSimulationType = string;
	export class EnumSimulation {
		static readonly VALUE_1:EnumSimulationType;
		static readonly VALUE_2:EnumSimulationType;
		static readonly VALUE_3:EnumSimulationType;

		values():string[];
	}

The the enum clients will use it like this

public void useEnum(@TsTypeRef(EnumSimulation.class) String param) {}

which will be exported like this

useEnum(param:EnumSimulationType):void;

The plan with the new Branded types pattern is to introduce a better typing for this, for example

this enum

package com.vertispan.tsdefs.tests.enums;

public enum Direction {

    LEFT,
    RIGHT,
    UP,
    DOWN
}

can be exported to typescript like this

declare const __brand: unique symbol
type Brand<B> = { [__brand]: B }
export type Branded<T, B> = T & Brand<B>

type DirectionType = Branded<string, "Direction">;

export class Direction {
    static readonly LEFT="LEFT" as DirectionType;
    static readonly RIGHT ="RIGH" as DirectionType;
    static readonly UP ="UP" as DirectionType;
    static readonly DOWN ="DOWN" as DirectionType;
}

export class DirectionClient {
    followDirection(direction: DirectionType) {
        console.info('Direction : '+direction);
    }
}

function testDirection(): string {
    let a = new DirectionClient();
    a.followDirection(Direction.LEFT)
    return "";
}

testDirection();

The type definition for this could be something like this :

declare const __brand: unique symbol
type Brand<B> = { [__brand]: B }
export type Branded<T, B> = T & Brand<B>

type DirectionType = Branded<string, "Direction">;

export class Direction {
    static readonly LEFT:DirectionType;
    static readonly RIGHT:DirectionType;
    static readonly UP:DirectionType;
    static readonly DOWN:DirectionType;
}

export class DirectionClient {
    followDirection(direction: DirectionType):void;
}
@vegegoku vegegoku added the enhancement New feature or request label Oct 18, 2023
@vegegoku vegegoku added this to the 1.1 milestone Oct 18, 2023
@vegegoku vegegoku self-assigned this Oct 18, 2023
@niloc132
Copy link
Member

Can you extract the .d.ts output for the above sample? Since there would be no quoted strong as a value, there probably is no need for an as, just

export class Direction {
    static readonly LEFT:DirectionType;
//...
}

etc?

@niloc132
Copy link
Member

Branded types could also be useful for decorating something that can't just be structurally typed - adding a @TsBranded or the like could signify that any class/interface declared should be already unioned with a unique symbol.

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

No branches or pull requests

2 participants