I’m learning Kotlin and for that reason I’m developing a browser extensions for firefox.
So therefore I’m making myself familiar with KotlinJs.
Now I’ve got a question about interop type mapping. This seems to confuse me a lot. To give you an example. Here is a class I’m working on currently, in order to store data for a browser extension
package core.storage
import core.utils.jsObject
import webextensions.browser
import kotlin.js.Promise
object StorageService {
const val INFO_KEY = "StorageInfos";
private inline fun List<StorageInfo>.toMap() = this.associateBy({ it.key }, { it }).toMutableMap()
private inline fun Map<String, StorageInfo>.toList() = this.map { it.value }
fun save(entry: StorageEntry): Promise<*> {
val pair = jsObject()
return loadInfos().then { infoList ->
val infoMap = infoList.toMap()
infoMap[entry.info.key] = entry.info
pair[INFO_KEY] = infoMap.toList()
pair[entry.info.key] = entry
}.then {
browser.storage.asDynamic().sync.set(pair)
}
}
// does not work
fun loadInfosBroken(): Promise<List<StorageInfo>> {
val resultsPromise = browser.storage.asDynamic().sync.get(INFO_KEY) as Promise<*>
return resultsPromise.then {
it.asDynamic()[INFO_KEY] as List<StorageInfo>? ?: ArrayList()
}
}
// works
fun loadInfos(): Promise<List<StorageInfo>> {
val resultsPromise = browser.storage.asDynamic().sync.get(INFO_KEY) as Promise<*>
return resultsPromise
.then { it.asDynamic()[INFO_KEY] as Array<StorageInfo>? ?: emptyArray() }.then { it.toList() }
}
fun clear(): Promise<*> {
return browser.storage.asDynamic().sync.clear() as Promise<*>
}
}
Method “loadInfosBroken” does not work. Somehow I can store data from a list but not retrieve it the same way. However I can retrieve list data as an array.
I wanted to work with a map, but also that seams to be confusing and not straight forward.
So my questions regarding that issue are:
Is there a good comprehensive guide which explains how objects from js to kotlin and vice versa do map?
Are there API’s which make working with these interop scenarios easiery?
val resultsPromise = browser.storage.asDynamic().sync.get(INFO_KEY) as Promise<*>
I’m casting the promise to (Promise<out Any?>)
I tried to cast it to Promise<dynamic> in order to avoid the asDynamic Call later. But that is not working as intelijii tells me that the inference is not working in that case.
So if you have an idea how to make the code less verbose, you are welcome
Now I’ve run into the next issue with types and kotlinJs … somehow the kotlin type and javascript type are not always mapped the same way, even though they appear like they are the same on the kotlin side
Here is my StorageInfo clas:
import kotlin.js.Date
data class StorageInfo(val key : String, val size : Int, val updated : Date)
So I’m using the date-class for java script of the kotlinjs standard library.
For printing the datetime I wrote myself a function:
fun Date.ToFormatedDateString() =
if (jsTypeOf(this) == "object")
"${getFullYear()}-${getMonth()}-${getDay()} ${getHours()}:${getMinutes()}:${getSeconds()}"
else jsTypeOf(this)
Here is an example how it looks with data loaded from the storage
The last line is not out of the storage but a new object.So if I call a method of the date class it crashes,
even though it appears to be type-safe.
Does anyone have an Idea how I can fix the serialization/deserialization?
Is it maybe more advisable to work with a json serialization framework? Are there kotlinjs annotations I could use to influence serialization/deserialization? I have not much experience with java script but I reckon JsObject and Json are not entirely the same, or are they?
Try kotlinx.serialization library from JetBrains. Make your data class serializable by adding @Serializable annotation. You can also easily create custom serializer for kotlin.js.Date.