hotwired / stimulus

A modest JavaScript framework for the HTML you already have

Home Page:https://stimulus.hotwired.dev/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Allow dispatch target type to not be the same as the controller element type

lb- opened this issue · comments

Summary

When using generics to declare a more specific element type on the controller, the dispatch target type is linked to that same type and it is no longer possible (without overrides or type checking warnings) to dispatch against a target of any kind of element.

Not sure if this is a bug but it is probably an unintended side effect of allowing a type signature for controllers via generics completed in #529

Steps to reproduce

  1. Create a new controller (TypeScript) similar to this, where the Controller's type is HTMLInputElement (e.g. not a generic Element or HTMLElement.
import { Controller } from '@hotwired/stimulus';

export class ExampleController extends Controller<HTMLInputElement> {
  connect() {
    const main = document.querySelector('main') as HTMLElement;

    this.dispatch('Connected!', { target: main });
  }
}
  1. Expected: I should be able to use the documented dispatch callback with any target element - as per https://stimulus.hotwired.dev/reference/controllers#cross-controller-coordination-with-events (see dispatch accepts additional options as the second parameter as follows:)

  2. Actual: I get a type error advising that the dispatch target element HTMLElement does not match the HTMLInputElement type.

Type 'HTMLElement' is missing the following properties from type 'HTMLInputElement': accept, align, alt, autocomplete, and 53 more.ts(2740)

A similar issue also happens if we were to pass in something like document to the target.

Screenshots

Screenshot 2023-01-24 at 11 08 17 am

Screenshot 2023-01-24 at 11 10 16 am

Potential solution

Allow the target type to be something like:

target?: ElementType | Element;

{ target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true } = {}

 dispatch(
    eventName: string,
    { target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true } = {}
  ) {
    const type = prefix ? `${prefix}:${eventName}` : eventName
    const event = new CustomEvent(type, { detail, bubbles, cancelable })
    target.dispatchEvent(event)
    return event
  }

This seems to work as expected now:

Screenshot 2023-01-24 at 15 07 56

Awesome. Thank you