Is your feature request related to a problem? Please describe.
I'd like to have typesafe event handlers, for this the type of the event has to be known beforehand.
Describe the solution you'd like
The createEventDispatcher function being extended with a generic and a parameter. The parameter would restrict the handler to only handle one kind of event, where the detail field's type would be determined by the type of the generic.
The new type signature of createEventDispatcher would look like this:
export declare function createEventDispatcher(): (type: string, detail?: any) => void;
export declare function createEventDispatcher<T>(type: string): (detail?: T) => void;
Example usage
child.svelte
<script lang="ts">
const fooDispatcher = createEventDispatcher<{ foo: string }>('foo');
fooDispatcher({ foo: 'hello' });
</script>
app.svelte
<script lang="ts">
import Child from './child.svelte';
function fooHandler(foo: { foo: string }) {}
function barHandler(bar: { bar: string }) {}
</script>
<Child on:foo={fooHandler} />
<Child on:foo={barHandler} /> <!-- Error, type mismatch! -->
<Child on:foo={(foo) => console.log(foo.foo)} /> <!-- Inferred type, intellisense -->
Possible problems
The svelte compiler has to be sure that only one createEventDispatcher have been defined for any given type, to prevent redefing the dispatcher with a different type. (This is easier if eventDispatchers are only allowed to be created on the top level and as const, but this would be a breaking change)
TypeScript can't protect against something like dispatching a malformed event through a regular eventDispatcher. To solve this either the untyped createEventDispatcher() would need to be deprecated, or the svelte compiler has to forbid the mixed usage of these.
Currently there is no way to export type definitions from components, so they have to be defined elsewhere and
import type-d into the component. It's a different topic but would increase usability if the type of events could be imported from the component directly.
How important is this feature to you?
It's important to provide type safety where it's possible.
I propose a different typing which would only change the typing but keep the implementation the same:
export declare function createEventDispatcher<EventMap extends {} = any>()
: <EventKey extends Extract<keyof EventMap, string>>(type: EventKey, detail?: EventMap[EventKey]) => void;
const bla = createEventDispatcher();
bla('anythingAllowedSinceNoEventMapTypingGiven', true);
const bla2 = createEventDispatcher<{click: boolean}>();
bla2('qwd', true); // <-- error, "qwd" not assignable to type "click"
bla2('click', ''); // <-- error, string not assignable to type boolean
But does this ensure in other components that when I listen to click I get a boolean? In other components the return type of the click event handlers has to be boolean?
This is an issue for the language-tools repo, this will not be type-checked if we don't add support for that in the language-tools repo. This also applies to your proposal.
Closing since the intent of this issue was to get type-safe event-handling. Dispatching in a type safe way within the component was added with the enhanced typings. Getting IDE support for typed event dispatchers on _other components_ will be work to do in the language-tools.
Svelte 3.25.0 now has createEventDispatcher with the types @dummdidumm mentioned above.
Could you reopen this? This is not what I described in this issue. The issue's title states "Typed event dispatcher and event handlers", not just event dispatchers
These new typings only restrict the events that have to be dispatched.
Take a look at what I described in the Example usage section of the issue, the important part is that on the recieving end I know what type of event can come out so the event handler is correct. Right now on 3.25.0 it's still CustomEvent<any>. In the example I denoted that barHandler in that case should cause an error, that is not happening right now.
Although I like the idea that you can define the types of the events per event in one type, I think my suggestion is needed for of the second part and thats why this issue is opened in this repository. The reason being that even if I were to bring the type of the event handler myself to the recieving end I can't extract correct types from this. Unless I define it explicitly, but inference is preferred.
const eh = createEventDispatcher<{ hello: { world: number }, foo: {bar: string} }>();
eh('hello', { world: 12 });
eh('foo', { bar: '12' });
function handle(a: Parameters<typeof eh>[1]) {
//a: {
// world: number;
//} | {
// bar: string;
//} | undefined
}
Even if I could define multiple event dispatchers (Technically I think I can) that defeats the purpose of being able to define the types of multiple events at once.
I stated above that this is something that needs to be handled in language tools. We will add support for it soon. That work can be tracked here https://github.com/sveltejs/language-tools/issues/424 .
I think it can work out with this kind of event dispatcher generics if it's possible in typescript to union the generics of all event dispatchers defined (In the case of svelte)
Most helpful comment
This is an issue for the
language-toolsrepo, this will not be type-checked if we don't add support for that in thelanguage-toolsrepo. This also applies to your proposal.