Skip to content

(feat) event typings #535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions docs/preprocessors/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,31 @@ Hit `ctrl-shift-p` or `cmd-shift-p` on mac, type `svelte restart`, and select `S

## Typing component events

When you are using TypeScript, you can type which events your component has by defining a reserved `interface` (_NOT_ `type`) called `ComponentEvents`:
When you are using TypeScript, you can type which events your component has in two ways:

The first and possibly most often used way is to type the `createEventDispatcher` invocation like this:

```html
<script lang="ts">
import { createEventDispatcher } from 'svelte';

const dispatch = createEventDispatcher<{
/**
* you can also add docs
*/
checked: boolean; // Will translate to `CustomEvent<boolean>`
hello: string;
}>();

// ...
</script>
```

This will make sure that if you use `dispatch` that you can only invoke it with the specified names and its types.

Note though that this will _NOT_ make the events strict so that you get type errors when trying to listen to other events when using the component. Due to Svelte's dynamic events creation, component events could be fired not only from a dispatcher created directly in the component, but from a dispatcher which is created as part of another import. This is almost impossible to infer.

If you want strict events, you can do so by defining a reserved `interface` (_NOT_ `type`) called `ComponentEvents`:

```html
<script lang="ts">
Expand Down Expand Up @@ -72,8 +96,6 @@ If you want to be sure that the interface definition names correspond to your di
</script>
```

> In case you ask why the events cannot be infered: Due to Svelte's dynamic nature, component events could be fired not only from a dispatcher created directly in the component, but from a dispatcher which is created as part of a mixin. This is almost impossible to infer, so we need you to tell us which events are possible.

## Troubleshooting / FAQ

### I cannot use TS inside my script even when `lang="ts"` is present
Expand Down Expand Up @@ -130,13 +152,13 @@ Create a `additional-svelte-jsx.d.ts` file:

```ts
declare namespace svelte.JSX {
interface HTMLAttributes<T> {
// If you want to use on:beforeinstallprompt
onbeforeinstallprompt?: (event: any) => any;
// If you want to use myCustomAttribute={..} (note: all lowercase)
mycustomattribute?: any;
// You can replace any with something more specific if you like
}
interface HTMLAttributes<T> {
// If you want to use on:beforeinstallprompt
onbeforeinstallprompt?: (event: any) => any;
// If you want to use myCustomAttribute={..} (note: all lowercase)
mycustomattribute?: any;
// You can replace any with something more specific if you like
}
}
```

Expand Down
13 changes: 5 additions & 8 deletions packages/svelte2tsx/src/svelte2tsx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import path from 'path';
import { convertHtmlxToJsx } from '../htmlxtojsx';
import { parseHtmlx } from '../utils/htmlxparser';
import { ComponentDocumentation } from './nodes/ComponentDocumentation';
import {
ComponentEvents,
ComponentEventsFromEventsMap,
ComponentEventsFromInterface,
} from './nodes/ComponentEvents';
import { ComponentEvents } from './nodes/ComponentEvents';
import { EventHandler } from './nodes/event-handler';
import { ExportedNames } from './nodes/ExportedNames';
import { createClassGetters, createRenderFunctionGetterStr } from './nodes/exportgetters';
Expand Down Expand Up @@ -179,6 +175,7 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult {
case 'Identifier':
handleIdentifier(node);
stores.handleIdentifier(node, parent, prop);
eventHandler.handleIdentifier(node, parent, prop);
break;
case 'Slot':
slotHandler.handleSlot(node, templateScope);
Expand Down Expand Up @@ -266,7 +263,7 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult {
moduleScriptTag,
scriptTag,
slots: slotHandler.getSlotDef(),
events: new ComponentEventsFromEventsMap(eventHandler),
events: new ComponentEvents(eventHandler),
uses$$props,
uses$$restProps,
uses$$slots,
Expand Down Expand Up @@ -472,7 +469,7 @@ export function svelte2tsx(
str,
uses$$propsOr$$restProps: uses$$props || uses$$restProps,
strictMode: !!options?.strictMode,
strictEvents: events instanceof ComponentEventsFromInterface,
strictEvents: events.hasInterface(),
isTsFile: options?.isTsFile,
getters,
fileName: options?.filename,
Expand All @@ -485,6 +482,6 @@ export function svelte2tsx(
code: str.toString(),
map: str.generateMap({ hires: true, source: options?.filename }),
exportedNames,
events,
events: events.createAPI(),
};
}
Loading