From f1d8ed64fd41995d869950092a9fe568b97a0596 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:56:51 +0200 Subject: [PATCH] fix: better types for `on` (#12053) - give autocompletion for types and more precise event types for html elements - allow to pass other kinds of event target nodes, like window closes #12027 fixes #12045 --- .changeset/rare-insects-tell.md | 5 ++++ .../internal/client/dom/elements/events.js | 30 +++++++++++++++++-- packages/svelte/tests/types/events.ts | 28 +++++++++++++++++ packages/svelte/tsconfig.json | 1 + packages/svelte/types/index.d.ts | 12 ++++++-- 5 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 .changeset/rare-insects-tell.md create mode 100644 packages/svelte/tests/types/events.ts diff --git a/.changeset/rare-insects-tell.md b/.changeset/rare-insects-tell.md new file mode 100644 index 0000000000..503219d2fd --- /dev/null +++ b/.changeset/rare-insects-tell.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: better types for `on` diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index a261eedba8..ea1cdfa419 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -33,7 +33,7 @@ export function replay_events(dom) { /** * @param {string} event_name - * @param {Element} dom + * @param {EventTarget} dom * @param {EventListener} handler * @param {AddEventListenerOptions} options */ @@ -71,7 +71,31 @@ export function create_event(event_name, dom, handler, options) { * rather than `addEventListener` will preserve the correct order relative to handlers added declaratively * (with attributes like `onclick`), which use event delegation for performance reasons * + * @template {HTMLElement} Element + * @template {keyof HTMLElementEventMap} Type + * @overload * @param {Element} element + * @param {Type} type + * @param {(this: Element, event: HTMLElementEventMap[Type]) => any} handler + * @param {AddEventListenerOptions} [options] + * @returns {() => void} + */ + +/** + * Attaches an event handler to an element and returns a function that removes the handler. Using this + * rather than `addEventListener` will preserve the correct order relative to handlers added declaratively + * (with attributes like `onclick`), which use event delegation for performance reasons + * + * @overload + * @param {EventTarget} element + * @param {string} type + * @param {EventListener} handler + * @param {AddEventListenerOptions} [options] + * @returns {() => void} + */ + +/** + * @param {EventTarget} element * @param {string} type * @param {EventListener} handler * @param {AddEventListenerOptions} [options] @@ -119,12 +143,12 @@ export function delegate(events) { } /** - * @param {Node} handler_element + * @param {EventTarget} handler_element * @param {Event} event * @returns {void} */ export function handle_event_propagation(handler_element, event) { - var owner_document = handler_element.ownerDocument; + var owner_document = /** @type {Node} */ (handler_element).ownerDocument; var event_name = event.type; var path = event.composedPath?.() || []; var current_target = /** @type {null | Element} */ (path[0] || event.target); diff --git a/packages/svelte/tests/types/events.ts b/packages/svelte/tests/types/events.ts new file mode 100644 index 0000000000..164d5f8acc --- /dev/null +++ b/packages/svelte/tests/types/events.ts @@ -0,0 +1,28 @@ +import { on } from 'svelte/events'; + +// ---------------- on + +on(document.body, 'click', (e) => e.button); + +on( + document.body, + 'clidck', + (e) => + // @ts-expect-error + e.button +); + +on( + window, + 'click', + (e) => + // @ts-expect-error ideally we'd know this is a MouseEvent here, too, but for keeping the types sane, we currently don't + e.button +); + +on( + // @ts-expect-error + 'asd', + 'asd', + (e) => e +); diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json index 062a5b1ece..5cb26aef5a 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/svelte/tsconfig.json @@ -19,6 +19,7 @@ "svelte": ["./src/index.d.ts"], "svelte/action": ["./src/action/public.d.ts"], "svelte/compiler": ["./src/compiler/public.d.ts"], + "svelte/events": ["./src/events/index.js"], "svelte/internal/client": ["./src/internal/client/index.js"], "svelte/legacy": ["./src/legacy/legacy-client.js"], "svelte/motion": ["./src/motion/public.d.ts"], diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index d80f35ca96..742e048c95 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -2350,9 +2350,15 @@ declare module 'svelte/events' { * rather than `addEventListener` will preserve the correct order relative to handlers added declaratively * (with attributes like `onclick`), which use event delegation for performance reasons * - * - */ - export function on(element: Element, type: string, handler: EventListener, options?: AddEventListenerOptions | undefined): () => void; + * */ + export function on(element: Element, type: Type, handler: (this: Element, event: HTMLElementEventMap[Type]) => any, options?: AddEventListenerOptions | undefined): () => void; + /** + * Attaches an event handler to an element and returns a function that removes the handler. Using this + * rather than `addEventListener` will preserve the correct order relative to handlers added declaratively + * (with attributes like `onclick`), which use event delegation for performance reasons + * + * */ + export function on(element: EventTarget, type: string, handler: EventListener, options?: AddEventListenerOptions | undefined): () => void; } declare module 'svelte/types/compiler/preprocess' {