Track parent tag of properties
cornerman opened this issue · 2 comments
Currently, the children properties of a tag do not know, into which kind of tag they are inserted. There is no parent relation of the types.
For example, we can currently write something like div(value := "bla")
, even though value is only defined on button, option, input, etc and not on div. Additionally, we can currently not type the currentTarget
property of events. This is always the object, on which the event listener was attached. So, if we write input(onChange := fun)
, we know that the type of currentTarget
is always html.Input
.
We would need a sane syntax for writing this. In scala I can only think about a macro for the tags
, that inject the correct types into the code or adds some implicit. Any other ideas?
Also see: outwatch/outwatch#93
Looks like there's two distinct problems here:
P1) Provide type of parent element to keys (attrs / props / etc.)
P2) Restrict type of parent element for each key
Regarding P1, see outwatch/outwatch#93 (comment) for a possible solution. The idea is that the key itself does not need the type, it's the :=
method that does, so maybe we can provide the type to it instead.
Regarding P2 – Suppose we could indeed define the type of value
as ApplesTo[ParentEl: dom.html.Button | dom.html.Input | dom.html.Option]
. We could then restrict the types provided in P1 to a subtype of ParentEl
, ensuring that we don't call value := x
on elements that don't support value
.
However, the ParentEl
type from P2 is by itself not very useful – e.g. we can't call .value
on such a type, and it only works in Scala.js.
So for things like currentTarget
typing, we would need P1 but not P2.
As an alternative to |
we can have multiple value attrs: def value: AppliesTo[dom.html.Input]
, def value: AppliesTo[dom.html.Button]
, etc. but this has its own problems, both for performance (need to instantiate many more objects) and convenience (can't do if (attr == value)
anymore).
Hmm instead of defining value
as ApplesTo[ParentEl: dom.html.Button | dom.html.Input | dom.html.Option]
we could define somewhat like this:
Note: I haven't tried to compile any of that. Not sure if it would work.
trait ValueAttr
trait AppliesTo[T, P] {
type Parent = P
}
implicit object valueAppliesToInput AppliesTo[Value, dom.html.Input]
implicit object valueAppliesToButton extends AppliesTo[Value, dom.html.Button]
lazy val value: Attr[String, ValueAttr]
And then:
class Attr[V, T] {
def :=[Parent](value: V)(implicit appliesTo: AppliesTo[T, Parent])
}
and similarly:
class EventProp[T] {
def :=[Parent](value: TargetedEvent[Parent] =>())(implicit appliesTo: AppliesTo[T, Parent])
}