JavaScript

JavaScript Interop

When working with JavaScript, i.e. creating a Kotlin application that compiles down to JavaScript, we often need to interoperate with existing libraries in JavaScript. While Kotlin already provides support for this, we’ve added a few more options in M10 to make interoperability even easier.

Dynamic support

In M10 we’ve added the dynamic keyword[1] which allows us to declare types as dynamic, permitting certain interoperability which previously could be more cumbersome. For instance, when working with jQuery, up to M10, our only option was to use the strongly typed libraries that Kotlin provides. As for M10 we can now use the dynamic keyword too

jquery.getJSON(KotlinCommitsURL) { commits ->
  val commitsTable = jquery("#kotlin-commits")
  commits.forEach { commit ->
     commitsTable.append("""
                    
                        ${commit.sha.substring(0, 6)}
                        ${commit.commit.message}
                    """)
  }
}

The code above calls getJSON function on jQuery to return a list of commits from GitHub. The function takes a lambda with a single parameter, which is the actual commits. Each entry in this list is in turn a commit entry with its own fields such as html_url or commit.message.

In the code jQuery, commits and commit are all dynamic, which means that anything we call on these will be resolved at runtime, i.e. by the JavaScript interpreter. This allows for two things:

  • Not have to use a strongly-typed library to work with jQuery
  • Be able to consume model that hasn’t previously been defined

The second ability is quite useful since it means that we don’t have to create intermediate strongly-typed classes to consume HTTP endpoints.

Of course, we could even use language constructs such as for loops to do the same thing, not only using the forEach extensions function.

jquery.getJSON(KotlinCommitsURL) { commits ->
  val commitsTable = jquery("#kotlin-commits")
  for(commit in commits) { 
    commitsTable.append("""
                    
                        ${commit.sha.substring(0, 6)}
                        ${commit.commit.message}
                    """)
   }
}

In order for this code to work however, we still need to declare jQuery as dynamic, and mark it with the corresponding native equivalent for Kotlin to call

native("$")
val jquery : dynamic = noImpl

The noImpl is required since non-nullable variables in Kotlin need initializing, which in this case would be throwing an exception, however this never occurs since it is effectively being compiled down to JavaScript and called on the client-side. The native annotation which already existed pre-M10 is telling Kotlin what the identifier is equivalent to in JavaScript.

Operators

When declaring dynamic types, certain operators act natively in JavaScript, such as for instance index accessors:

elements: dynamic
// in Kotlin
elements[1] 

would be compiled to:

elements[i]

in JavaScript.

Inlining JavaScript code

Another feature we added in M10 is the ability to inline some native JavaScript code in Kotlin code. We can do this using the js function:

       jquery.getJSON(KotlinCommitsURL) { commits ->
          js("console.log('Calling JavaScript')")  
          val commitsTable = jquery("#kotlin-commits")

The second line inserts console.log(‘Calling JavaScript’) in the output resulting from compilation, interlining JavaScript with Kotlin code.

Language Injections

M10 also added Language Injection support in IntelliJ IDEA for Kotlin. And while this applies to any string and any language, not just JavaScript, it definitely proves useful when using js, allowing this:

js-string

to look like this:

js-injected

when injecting JavaScript language:

inject-js

Summary

In addition to dynamic and js, we also introduced support for nativeGetter,nativeSetter and nativeInvoke annotations for JavaScript, which we already covered in the M10 release post.

These new features are all provided for better interoperability with JavaScript, but they do not trump any plans to continue to provide strongly-typed support for existing libraries and frameworks in JavaScript.

[1]
“dynamic” is a soft keyword:

  • if it occurs in a non-type context, it’s an identifier
  • in a type context, when followed by a dot (except for a dot that separates a receiver type from a function/property name) or an angle bracket <, it’s an identifier
  • on the left-hand-side of :: in a callable reference: dynamic::foo implies that dynamic there is a normal identifier
image description