Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Event binding & DOM property hints for attrs input #49

Open
barneycarroll opened this issue Aug 3, 2022 · 4 comments
Open

Event binding & DOM property hints for attrs input #49

barneycarroll opened this issue Aug 3, 2022 · 4 comments

Comments

@barneycarroll
Copy link
Member

Some forks of the hyperscript function recognise lifecycle methods as potential matches on the attrs input, but other than that, AFAICT, the interface is an Any — so unless the author is reaching for a lifecycle method, the contents of attrs are completely freeform, with no hints.

But there is a lot of further domain complexity to what can be described in attrs, and we could make the IDE experience much more powerful out of the box by providing hints for authors that describe some of these domains.

The matching sequence for Attrs ought IMO to behave as follows for the DOM element fork of the hyperscript function (non-element hyperscript forks are not affected by this proposal):

LifecycleMethod | Style | EventHandler | ElementProperty | Any
  • The remaining magic attribute, key, is of type Any anyway so merits no special consideration.
  • ElementProperty is AFAICT directly referencible via TypeScripts built-ins (Element is used to qualify vnode.dom in current types).
  • Style is its own discrete type, @panoply has this covered in Types for vnode style attribute #46
  • EventHandler is the type I think would bring the most value 👇

@boazblake spent a while looking for some logic design mistake in some code; but eventually it turned out to be an easily-missed typo — onclick: {/*contents*/} instead of onclick: () => {/*contents*/}. Exposing the type semantics for on-prefixed keys as being proxied to the DOM EventHandler interface would have caught this early. It turns out this is possible in TypeScript using the recent syntax addition:

interface ElementAttributes extends Element {
  [k: `on${string}`]: EventHandler,
  ...
}

I’ve never written TypeScript day to day but figure we could merge something like that 👆 into a rewritten Attributes.

If we can merge these things together without building an inheritance hierarchy, so much the better.

@spacejack what do you think?


External sources:

  1. The Mithril chat conversation that gave rise to this issue https://mithril.zulipchat.com/#narrow/stream/326810-help/topic/.E2.9C.94.20Spot.20the.20error/near/291786300
  2. My TypeScript chatroom help request thread on this subject https://discord.com/channels/508357248330760243/942074070860705852/1004379612387737670
  3. TypeScripts property key string matching feature announcement https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-4.html#symbol-and-template-string-pattern-index-signatures
  4. TypeScripts built-in DOM types https://github.com/microsoft/TypeScript/blob/main/lib/lib.dom.d.ts
@pygy
Copy link
Member

pygy commented Aug 5, 2022

+1 for [k: on${string}]: EventHandler if we can pull it out.

IIRC X | any coalesces to any so we'd have to be a bit smarter with this (maybe using negative logic to exclude the keys that are on the the other types).

But going loose with ElementProperties and a generic fallback seems like a reasonable route.

Otherwise, we'd need specialized types for every HTML and SVG element. This could probably be automated from IDLs. Also, we'd need to special case for and class (or more broadly take into account the HTML spec). If we were to go the strict route, [k: data-${x}]: any] could be used for custom attrs. Not sure going strict would be a good idea though.

@barneycarroll
Copy link
Member Author

I’m not sure if we could get per-element path forking to work — we’d need to query the selector input, and even if TS were capable of expressing that complexity, I don’t think eg for the event handler thing I can query the ${ string }.

It’s annoying there are these disparities in eg the shape of universally applicable common properties like className between HTML & SVG but I don’t think we can get around that with syntax features.

The maximalist approach would be to procedurally build definition imports from comprehensive granular references — ie maintain a mapping of all tagName : ElementInterface, eventName : EventTypeHandler etc. I think that’s too much work for diminishing returns.

@barneycarroll
Copy link
Member Author

IIRC X | any coalesces to any so we'd have to be a bit smarter with this (maybe using negative logic to exclude the keys that are on the the other types).

@pygy would this ever manifest as a problem? Surely if the fall through to any would only happen if the property hadn’t matched a more proximate definition, then there’s no logical possibility for a typed property to erroneously qualify as any?

@pygy
Copy link
Member

pygy commented Aug 5, 2022

IIRC, the | operator is not ordered in TS (edit: for types, for values it does shortcut evaluation of course), and any swallows narrower types.

# for free to subscribe to this conversation on GitHub. Already have an account? #.
Projects
None yet
Development

No branches or pull requests

2 participants