bsorrentino / java2typescript

Java Processor to generate Typescript Definition from Java classes - This is to help developing on JVM javascript engine (ie Nashorn) using Typescript
MIT License
34 stars 5 forks source link

Consider supporting GraalJS' foreign-object-prototype #34

Open pintomau opened 3 years ago

pintomau commented 3 years ago

As explained in the documentation https://www.graalvm.org/reference-manual/js/Options/#to-the-launcher

--js.foreign-object-prototype: provide JavaScript’s default prototype to foreign objects that mimic JavaScript’s own types (foreign Arrays, Objects and Functions). Boolean value, default is false.

So, for example, List could extend Array and so on.

bsorrentino commented 3 years ago

Hi @pintomau thanks for feedbak

Really this project is most related to generate Typescript compatible declarations for Java Object useful to make easier interoperability with Java if you'd like use Typescript to develop on top of GraalJS

Your suggestion is mostly for the GraalJS runtime and I'll take it in consideration in the examples

pintomau commented 3 years ago

Just want to add an example to this.

This flag makes it more natural to work with Java objects from the PoV of someone coming from the JS/TS world.

Instead of working with stream().map() or .get(0), you'd simply use .map() and [0].

Currently solved this in our code like this:

/**
 * This type represents the union of Java's List and Javascript's Array types.
 *
 * It's useful because we're using the js.foreign-object-prototype GraalJS option which
 * wraps the List type with a compatible Array API.
 *
 * @template E the concrete type hold by the list
 */
type JavaListProxy<E> = List<E> & [E]

// @ts-expect-error Java Lists are wrapped with a JS Array compatible type, therefore we're using an intersection type
const technicalEquipments: JavaListProxy<TechEquipmentItem> = vehicleItem.getTechnicalEquipments() ?? []
const techEquipment = technicalEquipments.find(...)

And by the way. Thanks a lot for the work you did here. Saved me a looot of time.

pintomau commented 3 years ago

Actually found a better way, it seems.

Create an overrides.d.ts file, add your own interface List<E> extends Array<E> (sort conflicts, I simply removed it, but ymmv), and add it last in the .tsconfig includes list.

bsorrentino commented 3 years ago

Hi @pintomau could you provide me a more complete example of that so I could integrate it inside processor ?

pintomau commented 3 years ago

Hi @bsorrentino , if you mean the overrides logic, then it looks like this:

declare namespace java.util {

  /**
   * List<E> overwrite that takes into account GraalJS' foreign-object-prototype where Lists are enriched with Javascript methods.
   *
   * Please note that due to conflicts, we're delegating sort, forEach, indexOf, lastIndexOf to JS' Array
   */
  interface List<E> extends Array<E>/* extends Collection<E> */ {

    // static copyOf<E>( arg0:Collection<E> ):List<E>;
    // static of<E>(  ):List<E>;
    // static of<E>( ...arg0:E[] ):List<E>;
    // static of<E>( arg0:E ):List<E>;
    // static of<E>( arg0:E, arg1:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E, arg5:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E, arg5:E, arg6:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E, arg5:E, arg6:E, arg7:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E, arg5:E, arg6:E, arg7:E, arg8:E ):List<E>;
    // static of<E>( arg0:E, arg1:E, arg2:E, arg3:E, arg4:E, arg5:E, arg6:E, arg7:E, arg8:E, arg9:E ):List<E>;
    add(arg0: E): boolean
    add(arg0: int, arg1: E): void
    addAll(arg0: Collection<E>): boolean
    addAll(arg0: int, arg1: Collection<E>): boolean
    clear(): void
    contains(arg0: any /* java.lang.Object */): boolean
    containsAll(arg0: Collection<any /* java.lang.Object */>): boolean
    equals(arg0: any /* java.lang.Object */): boolean
    // forEach<T>(arg0: Consumer<T>): void
    forEach(callbackfn: (value: E, index: number, array: E[]) => void, thisArg?: any): void;
    get(arg0: int): E
    // indexOf(arg0: any /* java.lang.Object */): int
    indexOf(searchElement: E, fromIndex?: number): number;
    isEmpty(): boolean
    iterator(): Iterator<E>
    // lastIndexOf(arg0: any /* java.lang.Object */): int
    lastIndexOf(searchElement: E, fromIndex?: number): number;
    listIterator(): any /* java.util.ListIterator */
    listIterator(arg0: int): any /* java.util.ListIterator */
    parallelStream(): java.util.stream.Stream<E>
    remove(arg0: any /* java.lang.Object */): boolean
    remove(arg0: int): E
    removeAll(arg0: Collection<any /* java.lang.Object */>): boolean
    removeIf(arg0: Predicate<E>): boolean
    replaceAll(arg0: UnaryOperator<E>): void
    retainAll(arg0: Collection<any /* java.lang.Object */>): boolean
    set(arg0: int, arg1: E): E
    size(): int
    // sort(arg0: any /* java.util.Comparator */): void
    sort(compareFn?: (a: E, b: E) => number): this;
    spliterator(): any /* java.util.Spliterator */
    stream(): java.util.stream.Stream<E>
    subList(arg0: int, arg1: int): List<E>
    toArray(): [any /* java.lang.Object */]
    toArray<T>(arg0: [T]): [T]
    toArray<T>(arg0: any /* java.util.function.IntFunction */): [T]

  } // end List

}

Notice that I had to comment some of Java's forEach, indexOf, lastIndexOf and sort methods because of clashing signatures with JS's Array.

And actually, override might not be the ideal name here. This does actually not replace the List type, but this works through Typescript's namespace merging, which I was not familiar with when I wrote the last comment. So, it's not the perfect solution.

bsorrentino commented 3 years ago

Hi @pintomau

I've deployed dev release 1.4-SNAPSHOT with fix.

please take a chance to test it an let me know

Thanks in advance

pintomau commented 3 years ago

Something's weird.

It's not correctly closing classes/interfaces/namespaces: image

installed localy from 9d1bfba4c41a272e410a7db908721c47d1927709

bsorrentino commented 3 years ago

Hi @pintomau

I've update dev release 1.4-SNAPSHOT with fix.

please take a chance to test it an let me know

Take note that to enable foreign-object-prototype yo have add the following processor options

<options>
    <ts.outfile>jdk8</ts.outfile>
        <compatibility>GRAALJS</compatibility>
        <foreignobjectprototype>true</foreignobjectprototype>
</options>

Thanks in advance.

pintomau commented 3 years ago

Yup, now everything is looking good.