You can use TypeScript within Svelte components. IDE extensions like the Svelte VSCode extension will help you catch errors right in your editor, and svelte-check
does the same on the command line, which you can integrate into your CI.
Setuppermalink
To use TypeScript within Svelte components, you need to add a preprocessor that will turn TypeScript into JavaScript.
Using SvelteKit or Vitepermalink
The easiest way to get started is scaffolding a new SvelteKit project by typing npm create svelte@latest
, following the prompts and choosing the TypeScript option.
ts
import {vitePreprocess } from '@sveltejs/kit/vite';constconfig = {preprocess :vitePreprocess ()};export defaultconfig ;
If you don't need or want all the features SvelteKit has to offer, you can scaffold a Svelte-flavoured Vite project instead by typing npm create vite@latest
and selecting the svelte-ts
option.
ts
import {vitePreprocess } from '@sveltejs/vite-plugin-svelte';constconfig = {preprocess :vitePreprocess ()};export defaultconfig ;
In both cases, a svelte.config.js
with vitePreprocess
will be added. Vite/SvelteKit will read from this config file.
Other build toolspermalink
If you're using tools like Rollup or Webpack instead, install their respective Svelte plugins. For Rollup that's rollup-plugin-svelte and for Webpack that's svelte-loader. For both, you need to install typescript
and svelte-preprocess
and add the preprocessor to the plugin config (see the respective READMEs for more info). If you're starting a new project, you can also use the rollup or webpack template to scaffold the setup from a script.
If you're starting a new project, we recommend using SvelteKit or Vite instead
<script lang="ts">permalink
To use TypeScript inside your Svelte components, add lang="ts"
to your script
tags:
<script lang="ts">
let name: string = 'world';
function greet(name: string) {
alert(`Hello, ${name}!`);
}
</script>
Propspermalink
Props can be typed directly on the export let
statement:
<script lang="ts">
export let name: string;
</script>
Slotspermalink
Slot and slot prop types are inferred from the types of the slot props passed to them:
<script lang="ts">
export let name: string;
</script>
<slot {name} />
<!-- Later -->
<Comp let:name>
<!-- ^ Inferred as string -->
{name}
</Comp>
Eventspermalink
Events can be typed with createEventDispatcher
:
<script lang="ts">
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher<{
event: null; // does not accept a payload
click: string; // has a required string payload
type: string | null; // has an optional string payload
}>();
function handleClick() {
dispatch('event');
dispatch('click', 'hello');
}
function handleType() {
dispatch('event');
dispatch('type', Math.random() > 0.5 ? 'world' : null);
}
</script>
<button on:click={handleClick} on:keydown={handleType}>Click</button>
Enhancing built-in DOM typespermalink
Svelte provides a best effort of all the HTML DOM types that exist. Sometimes you may want to use experimental attributes or custom events coming from an action. In these cases, TypeScript will throw a type error, saying that it does not know these types. If it's a non-experimental standard attribute/event, this may very well be a missing typing from our HTML typings. In that case, you are welcome to open an issue and/or a PR fixing it.
In case this is a custom or experimental attribute/event, you can enhance the typings like this:
ts
declare namespacesvelteHTML {// enhance elementsinterfaceIntrinsicElements {'my-custom-element': {someattribute : string; 'on:event': (e :CustomEvent <any>) => void };}// enhance attributesinterfaceHTMLAttributes <T > {// If you want to use on:beforeinstallprompt'on:beforeinstallprompt'?: (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}}
Then make sure that d.ts
file is referenced in your tsconfig.json
. If it reads something like "include": ["src/**/*"]
and your d.ts
file is inside src
, it should work. You may need to reload for the changes to take effect.
Since Svelte version 4.2 / svelte-check
version 3.5 / VS Code extension version 107.10.0 you can also declare the typings by augmenting the svelte/elements
module like this:
ts
import {HTMLButtonAttributes } from 'svelte/elements';declare module 'svelte/elements' {export interfaceSvelteHTMLElements {'custom-button':HTMLButtonAttributes ;}// allows for more granular control over what element to add the typings toexport interfaceHTMLButtonAttributes {veryexperimentalattribute ?: string;}}export {}; // ensure this is not an ambient module, else types will be overridden instead of augmented
Experimental advanced typingspermalink
A few features are missing from taking full advantage of TypeScript in more advanced use cases like typing that a component implements a certain interface, explicitly typing slots, or using generics. These things are possible using experimental advanced type capabilities. See this RFC for more information on how to make use of them.
The API is experimental and may change at any point
Limitationspermalink
No TS in markuppermalink
You cannot use TypeScript in your template's markup. For example, the following does not work:
<script lang="ts">
let count = 10;
</script>
<h1>Count as string: {count as string}!</h1> <!-- ❌ Does not work -->
{#if count > 4}
{@const countString: string = count} <!-- ❌ Does not work -->
{countString}
{/if}
Reactive Declarationspermalink
You cannot type your reactive declarations with TypeScript in the way you type a variable. For example, the following does not work:
<script lang="ts">
let count = 0;
$: doubled: number = count * 2; // ❌ Does not work
</script>
You cannot add a : TYPE
because it's invalid syntax in this position. Instead, you can move the definition to a let
statement just above:
<script lang="ts">
let count = 0;
let doubled: number;
$: doubled = count * 2;
</script>
Typespermalink
ComponentConstructorOptionspermalink
Svelte components were classes in Svelte 4. In Svelte 5, thy are not anymore. Use
mount
orcreateRoot
instead to instantiate components. See breaking changes for more info.
ts
interface ComponentConstructorOptions<Props extends Record<string, any> = Record<string, any>> {…}
ts
target: Element | Document | ShadowRoot;
ts
anchor?: Element;
ts
props?: Props;
ts
context?: Map<any, any>;
ts
hydrate?: boolean;
ts
intro?: boolean;
ts
$$inline?: boolean;
ComponentEventspermalink
Convenience type to get the events the given component expects. Example:
<script lang="ts">
import type { ComponentEvents } from 'svelte';
import Component from './Component.svelte';
function handleCloseEvent(event: ComponentEvents<Component>['close']) {
console.log(event.detail);
}
</script>
<Component on:close={handleCloseEvent} />
ts
type ComponentEvents<Comp extends SvelteComponent> =Comp extends SvelteComponent<any, infer Events>? Events: never;
ComponentPropspermalink
Convenience type to get the props the given component expects. Example:
<script lang="ts">
import type { ComponentProps } from 'svelte';
import Component from './Component.svelte';
const props: ComponentProps<Component> = { foo: 'bar' }; // Errors if these aren't the correct props
</script>
ts
type ComponentProps<Comp extends SvelteComponent> =Comp extends SvelteComponent<infer Props> ? Props : never;
ComponentTypepermalink
Convenience type to get the type of a Svelte component. Useful for example in combination with
dynamic components using <svelte:component>
.
Example:
<script lang="ts">
import type { ComponentType, SvelteComponent } from 'svelte';
import Component1 from './Component1.svelte';
import Component2 from './Component2.svelte';
const component: ComponentType = someLogic() ? Component1 : Component2;
const componentOfCertainSubType: ComponentType<SvelteComponent<{ needsThisProp: string }>> = someLogic() ? Component1 : Component2;
</script>
<svelte:component this={component} />
<svelte:component this={componentOfCertainSubType} needsThisProp="hello" />
ts
type ComponentType<Comp extends SvelteComponent> = (new (options: ComponentConstructorOptions<Comp extends SvelteComponent<infer Props>? Props: Record<string, any>>) => Comp) & {/** The custom element version of the component. Only present if compiled with the `customElement` compiler option */element?: typeof HTMLElement;};
EventDispatcherpermalink
ts
interface EventDispatcher<EventMap extends Record<string, any>> {…}
ts
<Type extends keyof EventMap>(...args: null extends EventMap[Type]? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions]: undefined extends EventMap[Type]? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions]: [type: Type, parameter: EventMap[Type], options?: DispatchOptions]): boolean;
Snippetpermalink
The type of a #snippet
block. You can use it to (for example) express that your component expects a snippet of a certain type:
ts
let {banner } =$props <{banner :Snippet <{text : string }> }>();
You can only call a snippet through the {@render ...}
tag.
ts
interface Snippet<T = void> {…}
ts
(arg: T): typeof SnippetReturn & {_: 'functions passed to {@render ...} tags must use the `Snippet` type imported from "svelte"';};
SvelteComponentpermalink
Can be used to create strongly typed Svelte components.
Example:permalink
You have component library on npm called component-library
, from which
you export a component called MyComponent
. For Svelte+TypeScript users,
you want to provide typings. Therefore you create a index.d.ts
:
ts
import {SvelteComponent } from "svelte";export classMyComponent extendsSvelteComponent <{foo : string}> {}
Typing this makes it possible for IDEs like VS Code with the Svelte extension to provide intellisense and to use the component like this in a Svelte file with TypeScript:
<script lang="ts">
import { MyComponent } from "component-library";
</script>
<MyComponent foo={'bar'} />
This was the base class for Svelte components in Svelte 4. Svelte 5+ components
are completely different under the hood. You should only use this type for typing,
not actually instantiate components with new
- use mount
or createRoot
instead.
See breaking changes
for more info.
ts
class SvelteComponent<Props extends Record<string, any> = any,Events extends Record<string, any> = any,Slots extends Record<string, any> = any> {…}
ts
[prop: string]: any;
ts
constructor(options: ComponentConstructorOptions<PropsWithChildren<Props, Slots>>);
- deprecated This constructor only exists when using the
asClassComponent
compatibility helper, which is a stop-gap solution. Migrate towards usingmount
orcreateRoot
instead. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more info.
ts
$destroy(): void;
- deprecated This method only exists when using one of the legacy compatibility helpers, which is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more info.
ts
$on<K extends Extract<keyof Events, string>>(type: K,callback: (e: Events[K]) => void): () => void;
- deprecated This method only exists when using one of the legacy compatibility helpers, which is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more info.
ts
$set(props: Partial<Props>): void;
- deprecated This method only exists when using one of the legacy compatibility helpers, which is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more info.
SvelteComponentTypedpermalink
Use
SvelteComponent
instead. See TODO for more information.
ts
class SvelteComponentTyped<Props extends Record<string, any> = any,Events extends Record<string, any> = any,Slots extends Record<string, any> = any> extends SvelteComponent<Props, Events, Slots> {}