TypeScript with Svelte
Turning on TypeScript
Section titled “Turning on TypeScript”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).
Typing props
Section titled “Typing props”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.
Snippet props and the Snippet type
Section titled “Snippet props and the Snippet type”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()}Generic components
Section titled “Generic components”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"]