TypeScript
Vous pouvez utiliser TypeScript dans vos composants Svelte. Les extensions d’IDE comme l’extension
VS Code de Svelte vous
permettent de visualiser les erreurs directement dans votre éditeur, et
svelte-check
fait la même chose en ligne de
commande, ce que vous pouvez utiliser dans votre CI.
<script lang="ts">
Pour utiliser TypeScript dans vos composants Svelte, ajoutez lang="ts"
à vos balises script
:
<script lang="ts">
let name: string = 'les gens';
function greet(name: string) {
alert(`Coucou ${name} !`);
}
</script>
<button onclick={(e: Event) => greet(e.target.innerText)}>
{name as string}
</button>
Faire cela vous permet d’utiliser les fonctionnalités de type de TypeScript, c’est-à-dire toutes les fonctionnalités qui disparaissent lorsque vous transpilez vers JavaScript, comme les annotations de type ou les déclarations d’interface. Les fonctionnalités qui demandent au compilateur TypeScript de générer du code ne sont pas supportées. Ceci inclut :
- l’utilisation d’enums
- l’utilisation des modificateurs
private
,protected
oupublic
dans les fonctions de constructeur couplée à l’initialisation de propriétés. - l’utilisation de fonctionnalités qui ne sont pas encore intégrées au standard ECMASCript (c-à-d qui ne sont pas en stage 4 du processus TC39), et donc pas encore implémentées dans Acorn, le parser que nous utilisons pour interpréter JavaScript.
Si vous souhaitez utiliser une de ces fonctionnalités, vous aurez besoin de mettre en place un
préprocesseur de script
.
Mise en place de préprocesseurs
Pour utiliser les fonctionnalités TypeScript non restreintes au type au sein de vos composants, vous aurez besoin d’ajouter un préprocesseur qui va transformer TypeScript en JavaScript.
import { function vitePreprocess(opts?: VitePreprocessOptions | undefined): import("svelte/compiler").PreprocessorGroup
vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const const config: {
preprocess: PreprocessorGroup;
}
config = {
// Notez le `{ script: true }` additionnel
preprocess: PreprocessorGroup
preprocess: function vitePreprocess(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 })
};
export default const config: {
preprocess: PreprocessorGroup;
}
config;
Utiliser SvelteKit ou Vite
La manière la plus simple de commencer est de pré-construire un nouveau projet SvelteKit avec npx sv create
, de suivre les instructions et de choisir l’option TypeScript.
import { function vitePreprocess(opts?: VitePreprocessOptions | undefined): import("svelte/compiler").PreprocessorGroup
vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const const config: {
preprocess: PreprocessorGroup;
}
config = {
preprocess: PreprocessorGroup
preprocess: function vitePreprocess(opts?: VitePreprocessOptions | undefined): import("svelte/compiler").PreprocessorGroup
vitePreprocess()
};
export default const config: {
preprocess: PreprocessorGroup;
}
config;
Si vous n’avez pas besoin ou ne souhaitez pas profiter de toutes les fonctionnalités que SvelteKit a
à offrir, vous pouvez également pré-construire un projet Svelte grâce à Vite en lançant npm create vite@latest
et en choisissant l’option svelte-ts
.
Dans les deux cas, un fichier svelte.config.ts
avec l’option vitePreprocess
sera ajouté à votre
projet. Vite/SvelteKit viendra lire ce fichier de configuration.
Autres outils de build
Si vous utilisez plutôt des outils comme Rollup ou Webpack, installez leurs plugins Svelte
respectifs. Pour Rollup il s’agit de
rollup-plugin-svelte, et pour Webpack il s’agit
de svelte-loader. Dans les deux cas vous aurez besoin
d’installer typescript
et svelte-preprocess
et d’ajouter le préprocesseur à la configuration de
plugin (référrez-vous aux READMEs respectifs pour plus d’infos). Si vous commencez un nouveau
projet, vous pouvez aussi utiliser le template rollup ou
webpack pour mettre en place votre projet à partir
d’un script.
Si vous commencez un nouveau projet, nous recommandons plutôt d’utiliser SvelteKit ou Vite.
Paramètres de tsconfig.json
Lorsque vous utilisez TypeScript, assurez-vous que votre fichier tsconfig.json
soit correctement
défini.
- Utilisez une
target
qui soit au moinsES2022
, ou bien unetarget
d’au moinsES2015
avecuseDefineForClassFields
. Ceci permet de s’assurer que les déclarations de rune dans les champs de classes restent intactes, pour permettre au compilateur de fonctionner correctement - Mettez
verbatimModuleSyntax
àtrue
pour conserver les imports tels quels - Mettez
isolatedModules
àtrue
pour que chaque fichier soit considéré en isolation. TypeScript a quelques fonctionnalités qui nécessitent une analyse croisée de fichiers ainsi qu’une compilation, ce que le compilateur de Svelte et des outils comme Vite ne font pas
Typer les $props
Vous pouvez typer les $props
comme un objet normal qui possèderait certaines propriétés.
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
propRequise: number;
propOptionnelle?: boolean;
snippetAvecUnArgumentString: Snippet<[string]>;
gestionnaireEvenement: (arg: string) => void;
[key: string]: unknown;
}
let {
requiredProperty,
optionalProperty,
snippetWithStringArgument,
eventHandler,
...everythingElse
}: Props = $props();
</script>
<button onclick={() => eventHandler('bouton cliqué')}>
{@render snippetWithStringArgument('coucou')}
</button>
$props génériques
Les composants peuvent déclarer des relations génériques entre leurs propriétés. Vous pouvez avoir
par exemple un composant de liste qui reçoit en props une liste d’éléments ainsi qu’un callback
ayant pour argument un élément de la liste. Pour déclarer que la propriété items
et le callback
select
traitent de données du même type, ajoutez l’attribut generics
à la balise script
:
<script lang="ts" generics="Item extends { text: string }">
interface Props {
items: Item[];
select(item: Item): void;
}
let { items, select }: Props = $props();
</script>
{#each items as item}
<button onclick={() => select(item)}>
{item.text}
</button>
{/each}
Le contenu de generics
est ce que vous mettriez entre les chevrons <...>
d’une fonction
générique. Autrement dit, vous pouvez utiliser plusieurs génériques, extends
, ainsi que des types
par défaut.
Typer des composants haut-niveau
Si vous construisez un composant qui englobe un élément natif, vous pourriez vouloir exposer tous
les attributs de l’élément sous-jacent à l’utilisateur. Dans ce cas, utilisez (ou héritez) l’une des
interfaces fournies par svelte/elements
. Voici un exemple pour un composant Button
:
<script lang="ts">
import type { HTMLButtonAttributes } from 'svelte/elements';
let { children, ...rest }: HTMLButtonAttributes = $props();
</script>
<button {...rest}>
{@render children?.()}
</button>
Tous les éléments n’ont pas nécessairement de définition de type dédiée. Pour ceux qui n’en ont pas,
utilisez SvelteHTMLElements
:
<script lang="ts">
import type { SvelteHTMLElements } from 'svelte/elements';
let { children, ...rest }: SvelteHTMLElements['div'] = $props();
</script>
<div {...rest}>
{@render children?.()}
</div>
Typer $state
Vous pouvez typer $state
comme toute autre variable.
let let count: number
count: number = function $state<0>(initial: 0): 0 (+1 overload)
namespace $state
$state(0);
Si vous ne fournissez pas de valeur initiale à $state
, son type sera en partie undefined
.
// Error: Type 'number | undefined' is not assignable to type 'number'
let let count: number
count: number = function $state<number>(): number | undefined (+1 overload)
namespace $state
$state();
Si vous savez que la variable sera définie avant que vous vous en serviez, castez cette variable
avec as
. Ceci est particulièrement utile lorsque vous utilisez des classes :
class class Counter
Counter {
Counter.count: number
count = function $state<number>(): number | undefined (+1 overload)
namespace $state
$state() as number;
constructor(initial: number
initial: number) {
this.Counter.count: number
count = initial: number
initial;
}
}
Le type Component
Les composants Svelte sont de type Component
. Vous pouvez vous en servir, ainsi que les types
associés, pour exprimer toute une variété de contraintes.
Cela sert notamment avec les composant dynamiques pour restreindre quels sont les composants qui peuvent leur être fournis :
<script lang="ts">
import type { Component } from 'svelte';
interface Props {
// seuls les composants qui ont au plus la propriété "prop"
// peuvent être fournis
ComposantDynamique: Component<{ prop: string }>;
}
let { ComposantDynamique }: Props = $props();
</script>
<ComposantDynamique prop="foo" />
Legacy mode
En Svelte 4, les composants étaient de type
SvelteComponent
Pour extraire le type des propriétés d’un composant, utilisez ComponentProps
.
import type { interface Component<Props extends Record<string, any> = {}, Exports extends Record<string, any> = {}, Bindings extends keyof Props | "" = 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
:
import type { Component } from 'svelte';
export declare const MyComponent: 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, type ComponentProps<Comp extends SvelteComponent | Component<any, any>> = Comp extends SvelteComponent<infer Props extends Record<string, any>, any, any> ? Props : Comp extends Component<infer Props extends Record<...>, any, string> ? Props : never
Convenience type to get the props the given component expects.
Example: Ensure a variable contains the props expected by MyComponent
:
import type { ComponentProps } from 'svelte';
import MyComponent from './MyComponent.svelte';
// Errors if these aren't the correct props expected by MyComponent.
const props: ComponentProps<typeof MyComponent> = { 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:
import type { Component, ComponentProps } from 'svelte';
import MyComponent from './MyComponent.svelte';
function withProps<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';
import type MonComposant = SvelteComponent<Record<string, any>, any, any>
const MonComposant: LegacyComponentType
MonComposant from './MonComposant.svelte';
function function withProps<TComponent extends Component<any>>(component: TComponent, props: ComponentProps<TComponent>): void
withProps<function (type parameter) TComponent in withProps<TComponent extends Component<any>>(component: TComponent, props: ComponentProps<TComponent>): void
TComponent extends interface Component<Props extends Record<string, any> = {}, Exports extends Record<string, any> = {}, Bindings extends keyof Props | "" = 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
:
import type { Component } from 'svelte';
export declare const MyComponent: 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) TComponent in withProps<TComponent extends Component<any>>(component: TComponent, props: ComponentProps<TComponent>): void
TComponent,
props: ComponentProps<TComponent>
props: type ComponentProps<Comp extends SvelteComponent | Component<any, any>> = Comp extends SvelteComponent<infer Props extends Record<string, any>, any, any> ? Props : Comp extends Component<infer Props extends Record<...>, any, string> ? Props : never
Convenience type to get the props the given component expects.
Example: Ensure a variable contains the props expected by MyComponent
:
import type { ComponentProps } from 'svelte';
import MyComponent from './MyComponent.svelte';
// Errors if these aren't the correct props expected by MyComponent.
const props: ComponentProps<typeof MyComponent> = { 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:
import type { Component, ComponentProps } from 'svelte';
import MyComponent from './MyComponent.svelte';
function withProps<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) TComponent in withProps<TComponent extends Component<any>>(component: TComponent, props: ComponentProps<TComponent>): void
TComponent>
) {}
// Affiche une erreur si le deuxième argument n'a pas les props attendues
// par le composant en premier argument
function withProps<LegacyComponentType>(component: LegacyComponentType, props: Record<string, any>): void
withProps(const MonComposant: LegacyComponentType
MonComposant, { foo: string
foo: 'bar' });
Pour déclarer qu’une variable attend le constructeur ou une instance d’un composant :
<script lang="ts">
import MyComponent from './MyComponent.svelte';
let componentConstructor: typeof MyComponent = MyComponent;
let componentInstance: MyComponent;
</script>
<MyComponent bind:this={componentInstance} />
Améliorer les types natifs du DOM
Svelte fait son maximum pour fournir les types de tous les éléments du DOM existants. Il se peut que parfois, vous vouliez utiliser des attributs expérimentaux ou des évènements personnalisés venant d’une action. Dans ces cas-là, TypeScript va lever une erreur, vous disant qu’il ne connait pas ces types. S’il s’agit d’attributs ou d’évènements standards non-expérimentaux, cela peut simplement être un oubli dans nos définitions de types HTML. Dans ce cas, vous pouvez ouvrir une issue et/ou une PR pour corriger le problème.
Dans le cas d’un attribut/évènement personnalisé ou expérimental, vous pouvez améliorer les types de cette manière :
declare namespace svelteHTML {
// améliorer les éléments
interface interface svelteHTML.IntrinsicElements
IntrinsicElements {
'mon-element-perso': { unattribut: string
unattribut: string; 'on:event': (e: CustomEvent<any>
e: interface CustomEvent<T = any>
CustomEvent<any>) => void };
}
// améliorer les attributs
interface interface svelteHTML.HTMLAttributes<T>
HTMLAttributes<function (type parameter) T in HTMLAttributes<T>
T> {
// si vous souhaitez utiliser l'évènement beforeinstallprompt
svelteHTML.HTMLAttributes<T>.onbeforeinstallprompt?: ((event: any) => any) | undefined
onbeforeinstallprompt?: (event: any
event: any) => any;
// If you want to use myCustomAttribute={..} (note: all lowercase)
// si vous souhaitez utiliser myCustomAttribute={..} (note: tout en minuscules)
svelteHTML.HTMLAttributes<T>.mycustomattribute?: any
mycustomattribute?: any; // vous pouvez remplacer any avec quelque chose de plus spécifique
}
}
Puis, assurez-vous que le fifhier .d.ts
est référencé dans votre fichier tsconfig.json
. S’il
possède quelque chose comme "include": ["src/**/*"]
et que votre .d.ts
se trouve dans le dossier
src
, cela devrait fonctionner. Il se peut que vous ayez besoin de recharger votre éditeur pour que
les changements soient pris en compte.
Vous pouvez aussi déclarer des types en augmentant le module svelte/elements
de la façon suivante
:
import { HTMLButtonAttributes } from 'svelte/elements';
declare module 'svelte/elements' {
export interface SvelteHTMLElements {
'bouton-perso': HTMLButtonAttributes;
}
// permet un contrôle plus granulaire sur l'élément sur lequel vous voulez ajouter des types
export interface HTMLButtonAttributes {
HTMLButtonAttributes.attributtresexperimental?: string | undefined
attributtresexperimental?: string;
}
}
export {}; // assure que ceci n'est pas un module ambiant, sans quoi les types seraient écrasés
Modifier cette page sur Github