android icon indicating copy to clipboard operation
android copied to clipboard

Consider support for extending native generic classes and providing generic arguments

Open vmutafov opened this issue 6 years ago • 3 comments

Currently, there is no way to implement for example java.util.List<T> and provide information what would be the type T. When extending such class/interface from TypeScript the following way:

@Interfaces([java.util.List])
class JSList implements java.util.List {
}

the static binding generator prior 5.3.1 would generate a proxy class with a similar signature: public class JSList implements java.util.List The introduced changes of the SBG in 5.3.1 generate public class JSList implements java.util.List<Object> having resolved the generic parameter T using its erasure (in this case java.lang.Object) as if it's a raw class inheritance.

With the changes of the SBG in 5.3.1, there is a way to feed it information about what would be the generic parameters when extending, however this would require some changes in the syntax of extending/implementing native classes:

@Native
class JSList implements java.util.List<java.lang.Integer>{
}

The above shown syntax would be possible with some changes in the webpack plugin and the js_parser.js in the SBG. In order to collect information about the generic arguments provided in the TS code there should be a custom transformer plugged into the webpack pipeline whose goal would be to lookup every TS node with a @Native decorator and preserve its extended class and implemented interfaces information somewhere. This could be stored by modifying the AST and creating internal decorators only for storage. Further, when the js_parser.js parses the JS code, it can look for the internal decorators and retrieve the information about the generics storing them for the SBG.

vmutafov avatar Apr 08 '19 16:04 vmutafov

Additional possibilities: https://github.com/NativeScript/android-runtime/issues/1355

vmutafov avatar Sep 13 '19 07:09 vmutafov

Changing the native class extend API could also be made to allow generating native methods which are not present in the class being extended. This could be done in this way, for example:

@Native("com.named.NativeList")
class NamedNativeList implements List{

    @NativeMethod
    add(data: string): void {
        throw new Error("Method not implemented.");
    }

}

@Native
class NativeList implements List{

    @NativeField
    data: string;

    nonNativeData: string;

    @NativeMethod
    add(data: string): void {
        throw new Error("Method not implemented.");
    }

    addAt(index: number): void{

    }

    @NativeExternalMethod
    getNativeData: (param1: String, param2: number) => String
}

In this example, fields annotated with @NativeField would be generated in the Java file as regular fields, methods annotated with @NativeMethod would be generated as regular methods and the lambda field annotated with @NativeExternalMethod would be generated as Java native method. Having Java native methods could allow easier use of JNA for accessing C/C++/Rust libraries.

vmutafov avatar Sep 13 '19 07:09 vmutafov

Supporting additional Java/Kotlin annotations to be set on the extending class could be possible, too. If we have for example the Java annotation:

@interface UserDefinedCustomJavaAnnotation {

    String value();

}

and we want to extend java.lang.Object setting the annotation in TypeScript:

@UserDefinedCustomJavaAnnotation({value: "some string data"})
class TSClass extends java.lang.Object{
}

The proposal for the syntax of the Java annotation usage in TypeScript is a little bit strange at first glance. This is because of the object {value: "some string data"}. Implementing the annotation support this way enables quite easy use of default annotation members and enable the user to set only what he needs. In this example, the TS decorator will then be translated by the SBG into @UserDefinedCustomJavaAnnotation(value = "some string data") and appending it to the generated Java mirror class. Of course, if the annotation's members are all marked as default, the user should be able to use the annotation as @UserDefinedCustomJavaAnnotation not needing to provide parameters.

If we want to support Java/Kotlin annotations, we should follow the definition of the annotations in Java. We must guarantee all possible types of annotation members could be used in TS.

vmutafov avatar Sep 13 '19 09:09 vmutafov