Mike Hogan
Mike Hogan

Reputation: 10603

Right Typescript type for on:change handler in Svelte

I have this code:

<select class="form-control" on:change={pathChanged}>

The signature for pathChanged is:

function pathChanged(event: { target: HTMLSelectElement }) {

When I run that through tsc using npm run check, I get this error:

Error: Type '(event: { target: HTMLSelectElement; }) => void' is not assignable to type 'FormEventHandler<HTMLSelectElement>'.
  Types of parameters 'event' and 'event' are incompatible.
    Type 'Event & { currentTarget: EventTarget & HTMLSelectElement; }' is not assignable to type '{ target: HTMLSelectElement; }'.
      Types of property 'target' are incompatible.
        Type 'EventTarget | null' is not assignable to type 'HTMLSelectElement'.
          Type 'null' is not assignable to type 'HTMLSelectElement'. (ts)

<select class="form-control" on:change={pathChanged}>

What signature should pathChanged have?

Upvotes: 9

Views: 9198

Answers (3)

exmaxx
exmaxx

Reputation: 3629

A convenient option is to use ChangeEventHandler:

const pathChanged: ChangeEventHandler<HTMLSelectElement> = (event) => {
  console.log(event.currentTarget)
}

This will correctly type event, event.target and event.currentTarget (use it in case the event bubbles up).

See the difference between target and currentTarget on MDN.

Upvotes: 1

brunnerh
brunnerh

Reputation: 184386

The event target is less specific1 than you would want it to be. In this scenario I usually use a type assertion within the function to work around this.

function pathChanged(event: Event) {
   const target = event.target as HTMLSelectElement;
   // ...
}

Though the error states that currentTarget should be typed correctly, so using that should work as well:

function pathChanged(event: { currentTarget: HTMLSelectElement })

If an event has a more specific type than just Event, e.g. MouseEvent, the types can be combined using &:

function onMouseUp(event: MouseEvent & { currentTarget: HTMLSelectElement })

1 The reason for that is that most events bubble and the target often can be any element within the element with the handler on it. currentTarget always refers to the element with the handler and thus can be typed definitively.

Upvotes: 21

Ender Bonnet
Ender Bonnet

Reputation: 163

I did this in an input type file:

// added this interface
interface FormEventHandler<T> {
    target: EventTarget | null;
}

// then in the function
const onChangeFile = async (event: FormEventHandler<HTMLInputElement>) => {
    const target = event.target as HTMLInputElement;
    // your code
}

Upvotes: 2

Related Questions