Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track parent tag of properties #13

Open
cornerman opened this issue Nov 17, 2017 · 2 comments
Open

Track parent tag of properties #13

cornerman opened this issue Nov 17, 2017 · 2 comments

Comments

@cornerman
Copy link
Contributor

cornerman commented Nov 17, 2017

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

@raquo
Copy link
Owner

raquo commented Nov 17, 2017

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).

@raquo
Copy link
Owner

raquo commented Nov 17, 2017

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])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
2 participants