If we are talking about issues. Yesterday I playe...
# javascript
g
If we are talking about issues. Yesterday I played with Firebase Functions and Kotlin. I’ve used Kotlin JS to write Firebase Functions (available only as node.js library for now) And found a very strange error on Firebase App initialisation when tried to use firebase-admin library. It crashed on runtime because of undefined INTERNAL field And I have this problem only If I use Kotlin module declaration
@file:JsModule("firebase-admin")
but not if I manually define
require
function and request module. After some investigations I’ve found a problem and the reason is not my code or library. firebase-admin written in typescript and way how TS defines classes and way how kotlin use them this is the problem. I could reproduce it on minimal example: this is a typescript class:
Copy code
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}
Compiled to JS:
Copy code
var Greeter = /** @class */ (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    return Greeter;
}());
So if you use this code in a straitforward way everything is fine:
Copy code
var greeter = new Greeter("world");
console.log(greeter.greet()); //Hello, world
But Kotlin in generated JS code caches object instance functions to variable and call them using this variable (I think to reduce generated code size). And this is the problem for code above. You can reproduce such problem in pure JS:
Copy code
var greeter = new Greeter("world");
var greet = greeter.greet;
console.log(greet); // [Function] -- so greet is function
console.log(greeter.greet()) // Hello, world -- works fine
console.log(greet()); // Hello, undefined -- undefined
So instead of reference to instance function we cached prototype function. Actually it means that Kotlin JS not compatible with TypeScript and some other pre-ES6 class implementations. Maybe I missed something, I do not work with JS for a long time and not familiar with TS. I can build later some more self containing example and create an issue.
k
Please, show the Kotlin code
But Kotlin in generated JS code caches object instance functions to variable and call them using this variable
Wrong. Kotlin/JS does not do this. Kotlin caches only some top-level declarations (classes, packages, class-level functions)..
g
Okay, I see. Yes I used it in top level declaration. Will try to show minimal example.
Copy code
//file: admin.kt
@file:JsModule("firebase-admin")
package admin

external fun initializeApp(options: dynamic): dynamic

//file: functions.kt
@file:JsModule("firebase-functions")
package functions

external fun config(): dynamic

//file main.kt
fun main(args: Array<String>) {
   //fully qualified name. `admin` can be imported, no difference
    admin.initializeApp(functions.config().firebase)
}
k
So you should not declare it as a package
Declare it as an object
Copy code
@JsModule("firebase-admin")
object FirebaseAdmin {
    fun initializeApp(options: dynamic): dynamic
}
g
Copy code
external val admin: dynamic
?
oh
I see, that make sense, thanks a lot
k
Perhaps we need a package-level annotation to mark that JsModule package members should not be cached.
g
Should
object FirebaseAdmin
be
external object FirebaseAdmin
instead?
k
yes
g
Unfortunately it was not clear for me how to define external class, because generated code looked fine
Thanks for explanation
Would be nice to have
external object
declaration as example in docs - https://kotlinlang.org/docs/reference/js-modules.html
I just not sure how to formulate this case correctly for documentation and how to distinct when you should use
external object
instead of
package