TypeScript
You can use TypeScript within Svelte components. IDE extensions like the Svelte VS Code 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.
<script lang="ts">
To use TypeScript inside your Svelte components, add lang="ts"
to your script
tags:
<scriptlang="ts">letname:string='world';functiongreet(name:string) {alert(`Hello,${name}!`);}</script><buttononclick={(e:Event)=>greet(e.target.innerText)}>{nameasstring}</button>
Doing so allows you to use TypeScript’s type-only features. That is, all features that just disappear when transpiling to JavaScript, such as type annotations or interface declarations. Features that require the TypeScript compiler to output actual code are not supported. This includes:
- using enums
- using
private
,protected
orpublic
modifiers in constructor functions together with initializers - using features that are not yet part of the ECMAScript standard (i.e. not level 4 in the TC39 process) and therefore not implemented yet within Acorn, the parser we use for parsing JavaScript
If you want to use one of these features, you need to setup up a script
preprocessor.
Preprocessor setup
To use non-type-only TypeScript features within Svelte components, you need to add a preprocessor that will turn TypeScript into JavaScript.
import{functionvitePreprocess(opts?:VitePreprocessOptions|undefined):import("svelte/compiler").PreprocessorGroup
vitePreprocess}from'@sveltejs/vite-plugin-svelte';constconstconfig:{preprocess:PreprocessorGroup;}
config={// Note the additional `{ script: true }`preprocess:PreprocessorGroup
preprocess:functionvitePreprocess(opts?:VitePreprocessOptions|undefined):import("svelte/compiler").PreprocessorGroup
vitePreprocess({VitePreprocessOptions.script?:boolean|undefined
preprocess script block with vite pipeline. Since svelte5 this is not needed for typescript anymore
script:true})};exportdefaultconstconfig:{preprocess:PreprocessorGroup;}
config;
Using SvelteKit or Vite
The easiest way to get started is scaffolding a new SvelteKit project by typing npx sv create
, following the prompts and choosing the TypeScript option.
import{functionvitePreprocess(opts?:VitePreprocessOptions|undefined):import("svelte/compiler").PreprocessorGroup
vitePreprocess}from'@sveltejs/vite-plugin-svelte';constconstconfig:{preprocess:PreprocessorGroup;}
config={preprocess:PreprocessorGroup
preprocess:functionvitePreprocess(opts?:VitePreprocessOptions|undefined):import("svelte/compiler").PreprocessorGroup
vitePreprocess()};exportdefaultconstconfig:{preprocess:PreprocessorGroup;}
config;
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.
In both cases, a svelte.config.js
with vitePreprocess
will be added. Vite/SvelteKit will read from this config file.
Other build tools
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
tsconfig.json settings
When using TypeScript, make sure your tsconfig.json
is setup correctly.
- Use a
target
of at leastES2022
, or atarget
of at leastES2015
alongsideuseDefineForClassFields
. This ensures that rune declarations on class fields are not messed with, which would break the Svelte compiler - Set
verbatimModuleSyntax
totrue
so that imports are left as-is - Set
isolatedModules
totrue
so that each file is looked at in isolation. TypeScript has a few features which require cross-file analysis and compilation, which the Svelte compiler and tooling like Vite don’t do.
Typing $props
Type $props
just like a regular object with certain properties.
<scriptlang="ts">importtype{ Snippet }from'svelte';interfaceProps{requiredProperty:number;optionalProperty?:boolean;snippetWithStringArgument:Snippet<[string]>;eventHandler:(arg:string)=>void;[key:string]:unknown;}let{requiredProperty,optionalProperty,snippetWithStringArgument,eventHandler,...everythingElse}:Props=$props();</script><buttononclick={()=>eventHandler('clicked button')}>{@rendersnippetWithStringArgument('hello')}</button>
Generic $props
Components can declare a generic relationship between their properties. One example is a generic list component that receives a list of items and a callback property that receives an item from the list. To declare that the items
property and the select
callback operate on the same types, add the generics
attribute to the script
tag:
<scriptlang="ts"generics="Itemextends{ text:string}">interfaceProps{items:Item[];select(item:Item):void;}let{ items,select }:Props=$props();</script>{#eachitemsasitem}<buttononclick={()=>select(item)}>{item.text}</button>{/each}
The content of generics
is what you would put between the <...>
tags of a generic function. In other words, you can use multiple generics, extends
and fallback types.
Typing wrapper components
In case you’re writing a component that wraps a native element, you may want to expose all the attributes of the underlying element to the user. In that case, use (or extend from) one of the interfaces provided by svelte/elements
. Here’s an example for a Button
component:
<scriptlang="ts">importtype{ HTMLButtonAttributes }from'svelte/elements';let{ children,...rest }:HTMLButtonAttributes=$props();</script><button{...rest}>{@renderchildren?.()}</button>
Not all elements have a dedicated type definition. For those without one, use SvelteHTMLElements
:
<scriptlang="ts">importtype{ SvelteHTMLElements }from'svelte/elements';let{ children,...rest }:SvelteHTMLElements['div']=$props();</script><div{...rest}>{@renderchildren?.()}</div>
Typing $state
You can type $state
like any other variable.
letletcount:number
count:number=function$state<0>(initial:0):0(+1overload)namespace$state
$state(0);
If you don’t give $state
an initial value, part of its types will be undefined
.
// Error: Type 'number | undefined' is not assignable to type 'number'letletcount:number
count:number=function$state<number>():number|undefined(+1overload)namespace$state
$state();
If you know that the variable will be defined before you first use it, use an as
casting. This is especially useful in the context of classes:
classclassCounter
Counter{Counter.count: number
count=function$state<number>():number|undefined(+1overload)namespace$state
$state()asnumber;constructor(initial:number
initial:number) {this.Counter.count: number
count=initial:number
initial;}}
The Component type
Svelte components are of type Component
. You can use it and its related types to express a variety of constraints.
Using it together with dynamic components to restrict what kinds of component can be passed to it:
<scriptlang="ts">importtype{ Component }from'svelte';interfaceProps{// only components that have at most the "prop"// property required can be passedDynamicComponent:Component<{ prop:string}>;}let{ DynamicComponent }:Props=$props();</script><DynamicComponentprop="foo"/>
Legacy mode
In Svelte 4, components were of type
SvelteComponent
To extract the properties from a component, use ComponentProps
.
importtype{interfaceComponent<PropsextendsRecord<string,any>={},ExportsextendsRecord<string,any>={},BindingsextendskeyofProps|""=string>
Can be used to create strongly typed Svelte components.
Example:
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
:
importtype{ Component }from'svelte';exportdeclareconstMyComponent:Component<{ 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'} />
Component,typeComponentProps<CompextendsSvelteComponent|Component<any,any>>=CompextendsSvelteComponent<inferPropsextendsRecord<string,any>,any,any>?Props:CompextendsComponent<inferPropsextendsRecord<...>,any,string>?Props:never
Convenience type to get the props the given component expects.
Example: Ensure a variable contains the props expected by MyComponent
:
importtype{ ComponentProps }from'svelte';importMyComponentfrom'./MyComponent.svelte';// Errors if these aren't the correct props expected by MyComponent.constprops:ComponentProps<typeofMyComponent>={ foo:'bar'};
In Svelte 4, you would do ComponentProps<MyComponent>
because MyComponent
was a class.
Example: A generic function that accepts some component and infers the type of its props:
importtype{ Component,ComponentProps }from'svelte';importMyComponentfrom'./MyComponent.svelte';functionwithProps<TComponent extends Component<any>>(component: TComponent,props: ComponentProps<TComponent>) {};// Errors if the second argument is not the correct props expected by the component in the first argument.withProps(MyComponent,{ foo:'bar'});
ComponentProps}from'svelte';importtypeMyComponent=SvelteComponent<Record<string,any>,any,any>constMyComponent:LegacyComponentType
MyComponentfrom'./MyComponent.svelte';functionfunctionwithProps<TComponentextendsComponent<any>>(component:TComponent,props:ComponentProps<TComponent>):void
withProps<function(type parameter)TComponentinwithProps<TComponentextendsComponent<any>>(component:TComponent,props:ComponentProps<TComponent>):void
TComponentextendsinterfaceComponent<PropsextendsRecord<string,any>={},ExportsextendsRecord<string,any>={},BindingsextendskeyofProps|""=string>
Can be used to create strongly typed Svelte components.
Example:
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
:
importtype{ Component }from'svelte';exportdeclareconstMyComponent:Component<{ 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'} />
Component<any>>(component:TComponent extends Component<any>
component:function(type parameter)TComponentinwithProps<TComponentextendsComponent<any>>(component:TComponent,props:ComponentProps<TComponent>):void
TComponent,props:ComponentProps<TComponent>
props:typeComponentProps<CompextendsSvelteComponent|Component<any,any>>=CompextendsSvelteComponent<inferPropsextendsRecord<string,any>,any,any>?Props:CompextendsComponent<inferPropsextendsRecord<...>,any,string>?Props:never
Convenience type to get the props the given component expects.
Example: Ensure a variable contains the props expected by MyComponent
:
importtype{ ComponentProps }from'svelte';importMyComponentfrom'./MyComponent.svelte';// Errors if these aren't the correct props expected by MyComponent.constprops:ComponentProps<typeofMyComponent>={ foo:'bar'};
In Svelte 4, you would do ComponentProps<MyComponent>
because MyComponent
was a class.
Example: A generic function that accepts some component and infers the type of its props:
importtype{ Component,ComponentProps }from'svelte';importMyComponentfrom'./MyComponent.svelte';functionwithProps<TComponent extends Component<any>>(component: TComponent,props: ComponentProps<TComponent>) {};// Errors if the second argument is not the correct props expected by the component in the first argument.withProps(MyComponent,{ foo:'bar'});
ComponentProps<function(type parameter)TComponentinwithProps<TComponentextendsComponent<any>>(component:TComponent,props:ComponentProps<TComponent>):void
TComponent>) {}// Errors if the second argument is not the correct props expected// by the component in the first argument.functionwithProps<LegacyComponentType>(component:LegacyComponentType,props:Record<string,any>):void
withProps(constMyComponent:LegacyComponentType
MyComponent,{foo:string
foo:'bar'});
To declare that a variable expects the constructor or instance type of a component:
<scriptlang="ts">importMyComponentfrom'./MyComponent.svelte';letcomponentConstructor:typeofMyComponent=MyComponent;letcomponentInstance:MyComponent;</script><MyComponentbind:this={componentInstance} />
Enhancing built-in DOM types
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:
declarenamespacesvelteHTML{// enhance elementsinterfaceinterfacesvelteHTML.IntrinsicElements
IntrinsicElements{'my-custom-element':{someattribute:string
someattribute:string;'on:event':(e:CustomEvent<any>
e:interfaceCustomEvent<T=any>
CustomEvent<any>)=>void};}// enhance attributesinterfaceinterfacesvelteHTML.HTMLAttributes<T>
HTMLAttributes<function(type parameter)TinHTMLAttributes<T>
T> {// If you want to use the beforeinstallprompt eventsvelteHTML.HTMLAttributes<T>.onbeforeinstallprompt?:((event:any)=>any)|undefined
onbeforeinstallprompt?:(event:any
event:any)=>any;// If you want to use myCustomAttribute={..} (note: all lowercase)svelteHTML.HTMLAttributes<T>.mycustomattribute?:any
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.
You can also declare the typings by augmenting the svelte/elements
module like this:
import{HTMLButtonAttributes}from'svelte/elements';declaremodule'svelte/elements'{exportinterfaceSvelteHTMLElements{'custom-button':HTMLButtonAttributes;}// allows for more granular control over what element to add the typings toexportinterfaceHTMLButtonAttributes{HTMLButtonAttributes.veryexperimentalattribute?:string|undefined
veryexperimentalattribute?:string;}}export{};// ensure this is not an ambient module, else types will be overridden instead of augmented
Edit this page on GitHub llms.txt