Skip to content

TypeScript with Svelte

Add lang="ts" to the script and you’re writing TypeScript. Runes are generic, so state is typed with no ceremony:

<script lang="ts">
let count = $state(0); // inferred number
let name = $state<string>(''); // explicit
let user = $state<User | null>(null);
</script>

The compiler infers the type from the initial value where it can; use $state<T>(...) to be explicit (e.g. when the initial value is null but the type is wider).

Declare an interface for your props and annotate the $props() destructuring. This is the idiomatic Svelte 5 replacement for Svelte 4’s individually-typed export let props:

<script lang="ts">
interface Props {
label: string;
count?: number; // optional
onclick?: () => void; // event callbacks are just props
}
let { label, count = 0, onclick }: Props = $props();
</script>
<button {onclick}>{label}: {count}</button>

Consumers get autocompletion and type-checking when they use the component, and svelte-check flags a wrong or missing prop at the call site.

Snippets passed as props are typed with the Snippet type from svelte. A snippet that takes parameters is Snippet<[ArgType]>:

<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
header: Snippet; // a plain snippet
row: Snippet<[item: string]>; // a snippet taking one argument
children: Snippet; // the default passed-in content
}
let { header, row, children }: Props = $props();
</script>
{@render header()}
{@render row('first')}
{@render children()}

When a component’s types depend on what the consumer passes (a typed list, a generic select), declare type parameters with the generics attribute on the script tag:

<script lang="ts" generics="T">
interface Props {
items: T[];
selected: T;
onselect: (item: T) => void;
}
let { items, selected, onselect }: Props = $props();
</script>

Now T flows through: pass items of User[] and onselect is typed as (item: User) => void. There’s also a Component type (from svelte) for typing a variable that holds a component.

flowchart LR
  props["interface Props"] --> dollar["let { ... }: Props = props()"]
  snip["Snippet type"] --> dollar
  gen["generics attribute (T)"] --> dollar
  dollar --> check["svelte-check verifies usage + markup"]
Where types flow in a Svelte component
How do you type a piece of state whose initial value does not reveal the full type?
What is the idiomatic way to type component props in Svelte 5?
What type describes a snippet passed as a prop?
How do you write a generic component whose types depend on the consumer's data?