Is there an easy way to make lightweight add-hoc J...
# javascript
d
Is there an easy way to make lightweight add-hoc JS objects with KJS? Lots of js libraries want objects as arguments to mimic optional arguments, so you'd call a function like this:
Copy code
libraryFunction({ optionalArg1: 'value', optionalArg2: true });
So far, I've been using this utility function:
Copy code
fun <T> obj(block: T.() -> Unit): T =
    js("{}").unsafeCast<T>().apply(block)
Which can be used like this:
Copy code
libraryFunction(obj { optionalArg1 = "value"; optionalArg2 = true })
Given the following external declarations:
Copy code
external interface Args {
    optionalArg1: String
    optionalArg2: Boolean
}

external fun libraryFunction(args: Args)
It's not 100% type safe since you can omit a required "argument" this way, but at least you can't mistype this way and you don't have to implement an entire interface that might contain dozens of properties of which you only really need 2 or 3. You also can't know which ones are required by reading the kotlin external declarations (but you can document this of course).
v
That's already existing
d
very strange, I don't seem to have it in scope, I'm using kotlin 1.4.20 (legacy js compiler)
v
iirc it is in
org.jetbrains:kotlin-extensions
2
d
yeah, seems to be nearly the same implementation as mine except that it's inline. might have to add that, probably simplifies the generated code, thanks for pointing me to that library
m
Copy code
json("optionalArg1" to "value", "optionalArg2" to true).asDynamic()
d
that's another good one to know, for when you need to build up some objects very dynamically
s
Type safe ad-hoc objects in Kotlin wold be:
Copy code
libraryFunction(object : Args {
		override optionalArg1 = "value"
		override optionalArg2 = true
})
If you use this method a lot, and this verbose syntax is all over the place, you can create a trivial class that implements this
Args
interface, or if possible, wrap this
libraryFunction
with a function with idiomatic Kotlin signature with default arguments:
Copy code
fun wrappedLibraryFunction(
	optionalArg1: String? = undefined,
    optionalArg2: Boolean? = undefined,
): Args = 
	object : Args {
		override optionalArg1 = optionalArg1
		override optionalArg2 = optionalArg2
	}
… you don’t have to implement an entire interface that might contain dozens of properties of which you only really need 2 or 3. You also can’t know which ones are required by reading the kotlin external declarations (but you can document this of course).
External interface you are using can be improved! With “default getter interface implementation” you don’t need to implement every member. Definitely helps with a dozen default members you don’t care about. This way default arguments are not only documented (in a bit weird way, but still) but are also enforced by type system.
Copy code
external interface Args {
    val optionalArg1: String? 
       get() = definedExternally
    val optionalArg2: Boolean? 
       get() = definedExternally
}

...
// all defaults!
libraryFunction(object : Args {})
Are you getting externals from dukat?
d
oh that's why Dukat generates all those getters and setters, I had't figured that out because I never implement those sort of interfaces to keep the generated code fast and small
but tbh I never looked at the code kjs generated for anonymous classes, maybe it's already pretty optimal
s
We can definitely improve generated code here. Currently we generate classes, constructors, etc. But if you have a pattern
object : <external-ifaces> { <overrides-of-external-interfaces> }
, I don’t see why we can’t just generate JS object literal like
{ optionalArg1: 'value', optionalArg2: true }
d
that would be great, it would give us both full type safety and performance/small code size
should I create a YT issue for this?
s
@Daan would be great!
d
t
I don’t see why we can’t just generate JS object literal like
Because described problem is not about
external interface
, but about missed
external options
and have more limitations
External options: 1. Could’t not be implemented by class 2. Must have only
read-only
properties 3. Must support optional properties 4. Can have ‘partial’ implementation
d
Point 2 doesn't seem like a real requirement, the compiler could see that you're not doing anything in the setter of a mutable property and then there's no problem in compiling to an object literal. So point 1 isn't valid either?
t
Point 2 doesn’t seem like a real requirement
In Kotlin options can be immutable by default (most used case) and have
copy
method Options used as parameter and parameter mutation - bad practice
So point 1 isn’t valid either?
It’s existed restriction, typical for
options
d
I don't think I understand your point, what's preventing the kotlin compiler from optimizing the most typical cases?
s
Hi @turansky, is
option
some kind of new Kotlin concept you proposing? Are they for JS interop only, or intended to be used in general? What advantage would they have over external interfaces?
It’s existed restriction, typical for 
options
 (edited)
Should work if we mark property optional. I still don’t see the a problem of implementing it by a class.
3. Must support optional properties
We can do this currently by abusing interface default implementation. I can see how this can be become nicer with annotation like
@JsOptional
or some modifier.
4. Can have ‘partial’ implementation
The same way Kotlin treats abstract and interface members? Or do you mean something else?
I still think that the best way to solve verbosity would be to add some general “tuple literal” that can be coerced to interfaces, similar to how lambda can be coerced to
fun interface
. With this feature, usage could look like this:
libraryFunction((optionalArg1: 'value', optionalArg2: true))
t
Are they for JS interop only, or intended to be used in general?
Yes, for JS interop only
I still don’t see the a problem of implementing it by a class.
Options has other inheritance rules For example options can be extended “partially” @Svyatoslav Kuzmich [JB] other details I will describe in issue - it will be more productive
s
looking forward to read more
t