0

I create alot of non-native JS trait instances as parameters to javascript functions

trait ElementOpts extends js.Object {
  val prop1: js.UndefOr[String] = js.undefined
  // another 10-15 vals
}

To create an instance:

createElement(new ElementOpts {
  override val prop1 = "value1"
  override val prop2 = "value2" 
  // several more, maybe not *all* possible vals
})

However, there is alot typing noise in defining these with the override vals, etc.

It would be nice just to just type a list of key-value pairs and have that turned into the trait which is typechecked so you cannot accidentally add a val that is not defined in the trait:

lit[ElementOpts](prop1="value1",prop2="value2")

and have that turned into the above "new ElementOpts" syntax. I used "lit" to be suggestive of a javascript literal object.

Can scalameta do this? If so, is there an example of something similar? Or should this be handled in a pre-processor of some sort.

user1763729
  • 167
  • 1
  • 11
  • Note that there have been comments before around this [here](https://stackoverflow.com/questions/26638171/how-do-i-create-options-objects-in-scala-js?rq=1) and [here](https://github.com/jducoeur/jsext). – user1763729 Feb 10 '18 at 03:52

1 Answers1

0

Define your ElementOpts with vars instead of vals, and then you can simply do:

createElement(new ElementOpts {
  prop1 = "value1"
  prop2 = "value2"
  // several more, maybe not *all* possible vars
})
Adowrath
  • 701
  • 11
  • 24
sjrd
  • 21,805
  • 2
  • 61
  • 91
  • That works for me. Just to note for others, if you have a required attribute you can still set that as a "val requiredProp: String" to force it to be present under "new". – user1763729 Feb 10 '18 at 14:01
  • If I need type parameters though I still have to use a def which becomes a getter which will not work though...so how does that apply to a def "trait ElementOpts[T <: dom.EventTarget] { def onKeyDown[U >: T <: dom.EventTarget]: js.UndefOr[KeyboardEventHandler[U]] = js.undefined; ..."? I know I did not ask about defs in the initial question but it applies as we need to create type safe objects that has "fields". – user1763729 Feb 10 '18 at 14:06
  • Maybe we just need to find a way to take away the type constraints in order to get "field present" checks vs some type checks--which may not be a bad trade-off. – user1763729 Feb 10 '18 at 14:31
  • Make it a `val`/`var` of type `js.FunctionN`, as usual. – sjrd Feb 10 '18 at 16:23
  • It's a type constraint on the def since the T parameter to the trait is covariant and I needed it contravariant as it maps into a contravariant position, "type KeyboardEventHandler[T <: dom.EventTarget] = EventHandler[T, SyntheticKeyboardEvent[T]] and " type EventHandler[T <: dom.EventTarget, E <: SyntheticEvent[T]] = E => Unit " . This allows you to use a more general event object in the handler and still have it typecheck as SyntheticKeyboardEvent is covariant in its parameter since it is returned from some of the event methods. – user1763729 Feb 10 '18 at 16:34
  • sjrd's approach seems to work well without a macro. To get around the variance issue that results in turning the def (with type parameters) into a val, I added a @uncheckedVariance annotation to the type parameter for the "handler vals": val onKeyDown: js.UndefOr[KeyboardEventHandler[T @uncheckedVariance]] = js.undefined – user1763729 Feb 11 '18 at 21:42