hilla icon indicating copy to clipboard operation
hilla copied to clipboard

Support @JsonCreator and @JsonValue when generating TypeScript from Java

Open peholmst opened this issue 1 year ago • 2 comments

Describe your motivation

In Java, it is a good practice to use domain primitives rather than ordinary primitives in certain situations. For instance, instead of using a String or long as the customer ID, you should create a CustomerId type that wraps a String or long. It should be possible to use domain primitives like this in @BrowserCallable services.

Describe the solution you'd like

Although having domain primitives in TypeScript would be nice, it isn't as important as having them in Java. I would be fine with a solution where I can use domain primitives in Java and basic types in TypeScript. The easiest way to do this would be to support @JsonValue and @JsonCreator on the Java side.

My CustomerId class would have a method (maybe toString()) annotated with @JsonValue. An outgoing CustomerId object would then be serialized to a string in TypeScript.

I would also have a corresponding constructor that accepts a single String parameters and is annotated with @JsonCreator. An incoming TypeScript string would then be deserialized to a CustomerId object.

Describe alternatives you've considered

I could make separate @BrowserCallable services and DTOs that use ordinary primitives and do the conversion manually on the Java side before delegating to the "real" services and DTOs that use domain primitives. However, this feels like unnecessary complexity if the only difference between these services and DTOs is that the domain primitives have been replaced with their wrapped ordinary primitives.

Additional context

No response

peholmst avatar Feb 08 '24 15:02 peholmst

Hilla generator does not distinguish input and output types. So we need to provide support for both at the same time.

Let's take @JsonValue into account when emitting TypeScript type. The @JsonCreator paired with @JsonValue would be supported automatically by Jackson runtime.

platosha avatar Feb 13 '24 12:02 platosha

Hilla generator does not distinguish input and output types. So we need to provide support for both at the same time.

Let's take @JsonValue into account when emitting TypeScript type. The @JsonCreator paired with @JsonValue would be supported automatically by Jackson runtime.

platosha avatar Feb 13 '24 12:02 platosha

I don't know how feasible it is at code level, but would it be reasonable to automatically unpack all single-property objects when dealing with TS? That would mean that record Email(string value) would be translated to a string in TS, without any additional annotation required.

I think that it would be a nice way to simplify developers' life with those types.

The main drawback is that, if the object becomes record Email(String value, boolean verified), a new Email type is introduced in TS and client code must be modified. But I'm not sure that this kind of change would happen frequently except in early development: single-property objects are likely domain primitives.

cromoteca avatar Apr 24 '24 12:04 cromoteca

Automatic handling of single-property objects might make sense but I suspect we would still also need to support @JsonCreator and @JsonValue to allow customization.

Legioth avatar Apr 24 '24 13:04 Legioth

In both cases, there are some further aspects that must be taken into account. One is different nullability between types.

For example, if a Person has a nullable email property of type Email, and Email has a non-nullable property of type String, it's easy to guess that the email must stay nullable. The opposite would be problematic, as it would allow to assign a null value to a property which is not nullable, so domain primitives should host a non-nullable field.

Generics must be taken into account too: suppose we have a Id<T> class and a Person class that has and id field of type Id<Long>. We must be able to capture the Long type.

cromoteca avatar Apr 30 '24 07:04 cromoteca