Skip to content

Rethink how overloads are handledΒ #743

@Rich-Harris

Description

@Rich-Harris

At the moment, when a function has overloads, we only render the first description β€” see for example svelte/events#on, where we display this...

Attaches an event handler to the window 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

...and disregard all the subtly different ones. The result is that the documentation is incorrect unless you're attaching listeners to window.

Another example is svelte/store#derived, where instead of this (which is gibberish in any case!)...

image

...it might be nice to have something like this, which although a bit repetitive is arguably more comprehensible than what we have now:


derived

This function has four overloads.

  1. Create a Readable store whose value is derived from another store and a fn, which is called when it is first subscribed to and whenever the value of store changes thereafter as long as it has subscribers.

    const count = writable(0);
    const doubled = derived(count, ($count) => $count * 2);
    function derived<S, T>(
      store: Readable<S>,
      fn: (value: S) => T
    ): Readable<T>;
  2. Create a Readable store whose value is derived from multiple stores and a fn, which is called when it is first subscribed to and whenever any of the store values change thereafter as long as it has subscribers.

    const a = writable(1);
    const b = writable(2);
    const sum = derived([a, b], ($a, $b) => $a + $b);
    function derived<Stores extends Readable<any>[], T>(
      stores: Stores,
      fn: (values: Values<Stores>) => T
    ): Readable<T>;
  3. Create a Readable store whose value is updated by calls to set and update arguments provided to fn, which is called when it is first subscribed to and whenever the value of store changes thereafter as long as it has subscribers.

    If fn returns a function, it will be called before fn re-runs, and when there are no more subscribers, allowing you to perform cleanup work.

    An optional third argument provides an initial value, if set/update are called asynchronously.

    const count = writable(0);
    const delayed = derived(count, ($count, set) => {
      const timeout = setTimeout(() => {
        set($count);
      }, 1000);
    }, 0);
    function derived<S, T>(
      store: Readable<S>,
      fn: (
        value: S,
        set: (value: T) => void,
        update: (fn: Updater<T>) => void
      ) => Unsubscriber | void,
      initial?: T | undefined
    ): Readable<T>;
  4. Create a Readable store whose value is updated by calls to set and update arguments provided to fn, which is called when it is first subscribed to and whenever the value of stores change thereafter as long as it has subscribers.

    If fn returns a function, it will be called before fn re-runs, and when there are no more subscribers, allowing you to perform cleanup work.

    An optional third argument provides an initial value, if set/update are called asynchronously.

    const a = writable(1);
    const b = writable(2);
    const delayed = derived([a, b], ([$a, $b], set) => {
      const timeout = setTimeout(() => {
        set($a + $b);
      }, 1000);
    }, 0);
    function derived<Stores extends Readable<any>[], T>(
      stores: Readable<S>,
      fn: (
        value: Values<Stores>,
        set: (value: T) => void,
        update: (fn: Updater<T>) => void
      ) => Unsubscriber | void,
      initial?: T | undefined
    ): Readable<T>;

Perhaps more importantly, having separate overloads like this would mean more appropriate inline documentation (though TS will default to the first overload, in cases where inference isn't yet possible).

Above all, this has reminded me how much I hate overloads.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions