5

I'm trying to implement TypeScript into Svelte and has a problem like this: when I try to add type to an event in beneath line:

on:click={(e: Event) => onClick(e, data)}

it yells about:

Error: ParseError: Unexpected token

If I remove typing it says that:

Parameter 'e' implicitly has an 'any' type.

How can I add type to this kind of things without an error in Svelte?

EDIT: More complex example:

{#each elementsArray as element}
      <CustomComponent
        on:itemClick={(e: Event) => doSomething(e, element)}>
      </CustomComponent>
    {/each}

3 Answers 3

6

According to the official docs, there's no official support for TypeScript in the template.

At the moment, you cannot [use Typescript inside the template/mustache tags]. Only script/style tags are preprocessed/transpiled.

You need to move the typecasting to the <script> tag.

<script lang="ts">
  function onClick(e: MouseEvent) { ... }
</script>
<button on:click={onClick}></button>

In case your event is coming as a custom event from a child using createEventDispatcher, your e event will be typed as CustomEvent<any> and you can typecast it in the <script> as you please.

<script lang="ts">
  let elems = [1,2,3];
  
  function onClick(e: CustomEvent<{foo: string}>, el: number) {
    console.log(e.detail.name);
  }
<script>
{#each elems as el}
  <CustomComponent on:itemClick={e => onClick(e, el)}></CustomComponent>
{/each}

In case you still get errors for implicit any, try turning off noImplicitAny in your tsconfig.json.

Sign up to request clarification or add additional context in comments.

4 Comments

But one question about that - what if I have some more properties to pass? Not only an event? Like on:click=((e) => onClick(e, someThing))
Where's that someThing coming from? is that a prop from the script? It seems to me like it's not coming from the template so I think you can just use someThing in the onClick function as a "global". It won't feel as clean, though.
Carlo, I've updated original post andd added some more complex code that explains what I'm aiming at. In that case don't know how to pass info about what element I'm clicking.
I've updated my answer to handle the more complex case. I'm not getting errors for e being any.
0

There are a number of bugs files on this issue, e.g. svelte/4701

Comments

0

Let me suggest some workarounds for this Svelte/Typescript problem.

First two basic variants:

let elementsArray = ["foo", "bar", "lorem", "ipsum"];

function doSomething(evt: Event, element: string) {
    console.log("doSomething() called", evt, element);
}

function callbackFactory1(element: string) {
    return (evt: Event) => {
        doSomething(evt, element);
    };
}

function callbackFactory2(callback: Function, ...args: unknown[]) {
    return (evt: Event) => {
        callback(evt, ...args);
    };
}

Then use the callback factories like this:

{#each elementsArray as element}
    <p><a on:click={callbackFactory1(element)} href="#">{element}</a></p>
    <p><a on:click={callbackFactory2(doSomething, element)} href="#">{element}</a></p>
{/each}

What happens is that the callbackFactory() functions are called when Svelte is setting up the component, and the functions that they return are then assigned as the event handlers.

The first one (callbackFactory1()) obviously has more type safety but is also less flexible. You have to create new callback factories for every callback.

The second one is more flexible but has less type safety. You can use it with any callback function and any set of arguments, but beware of type bugs!


Now, if we make this a little bit more complicated with currying, we can actually get both type safety and flexibility at the same time!

function curriedDoSomething(evt: Event) {
    return (element: string) => {
        console.log("curriedDoSomething() called", evt, element);
        // Either call doSomething() or implement logic here
    };
}

function callbackFactory3<TE extends Event, TA extends unknown[]>(callback: (evt: TE) => (...args: TA) => void) {
    return (...args: TA) => {
        return (evt: TE) => {
            callback(evt)(...args);
        };
    };
}

It may look complicated, but the gist of it is that we return a chain of functions; one that receives the event, and then another that receives "the other arguments".

By creating separate functions for these groups of arguments, we achieve type safety in Typescript. (Maybe it's possible some other way, but I was unsuccessful.)

<a on:click={callbackFactory3(curriedDoSomething)(element)} href="#">{element}</a>

The downside of this is of course that you must curry your functions, but the upside is that Svelte and Typescript will make sure that you're expecting the correct Event and callback arguments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.