Skip to main content

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 ou public 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.

svelte.config
import { function vitePreprocess(opts?: VitePreprocessOptions | undefined): import("svelte/compiler").PreprocessorGroupvitePreprocess } from '@sveltejs/vite-plugin-svelte';

const 
const config: {
    preprocess: PreprocessorGroup;
}
config
= {
// Notez le `{ script: true }` additionnel preprocess: PreprocessorGrouppreprocess: function vitePreprocess(opts?: VitePreprocessOptions | undefined): import("svelte/compiler").PreprocessorGroupvitePreprocess({ VitePreprocessOptions.script?: boolean | undefined

preprocess script block with vite pipeline. Since svelte5 this is not needed for typescript anymore

@defaultfalse
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.

svelte.config
import { function vitePreprocess(opts?: VitePreprocessOptions | undefined): import("svelte/compiler").PreprocessorGroupvitePreprocess } from '@sveltejs/vite-plugin-svelte';

const 
const config: {
    preprocess: PreprocessorGroup;
}
config
= {
preprocess: PreprocessorGrouppreprocess: function vitePreprocess(opts?: VitePreprocessOptions | undefined): import("svelte/compiler").PreprocessorGroupvitePreprocess() }; 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 moins ES2022, ou bien une target d’au moins ES2015 avec useDefineForClassFields. 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: numbercount: number = 
function $state<0>(initial: 0): 0 (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$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: numbercount: number = 
function $state<number>(): number | undefined (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$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 CounterCounter {
	Counter.count: numbercount = 
function $state<number>(): number | undefined (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$state
() as number;
constructor(initial: numberinitial: number) { this.Counter.count: numbercount = initial: numberinitial; } }

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&#x3C;{ 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:

&#x3C;script lang="ts">
	import { MyComponent } from "component-library";
&#x3C;/script>
&#x3C;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&#x3C;typeof MyComponent> = { foo: 'bar' };

In Svelte 4, you would do ComponentProps&#x3C;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&#x3C;TComponent extends Component&#x3C;any>>(
	component: TComponent,
	props: ComponentProps&#x3C;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>): voidwithProps<function (type parameter) TComponent in withProps<TComponent extends Component<any>>(component: TComponent, props: ComponentProps<TComponent>): voidTComponent 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&#x3C;{ 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:

&#x3C;script lang="ts">
	import { MyComponent } from "component-library";
&#x3C;/script>
&#x3C;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>): voidTComponent, 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&#x3C;typeof MyComponent> = { foo: 'bar' };

In Svelte 4, you would do ComponentProps&#x3C;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&#x3C;TComponent extends Component&#x3C;any>>(
	component: TComponent,
	props: ComponentProps&#x3C;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>): voidTComponent>
) {} // 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>): voidwithProps(const MonComposant: LegacyComponentTypeMonComposant, { foo: stringfoo: '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 :

additional-svelte-typings.d
declare namespace svelteHTML {
	// améliorer les éléments
	interface interface svelteHTML.IntrinsicElementsIntrinsicElements {
		'mon-element-perso': { unattribut: stringunattribut: 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) | undefinedonbeforeinstallprompt?: (event: anyevent: any) => any;
		// If you want to use myCustomAttribute={..} (note: all lowercase)
		// si vous souhaitez utiliser myCustomAttribute={..} (note: tout en minuscules)
		svelteHTML.HTMLAttributes<T>.mycustomattribute?: anymycustomattribute?: 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 :

additional-svelte-typings.d
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 | undefinedattributtresexperimental?: string;
	}
}

export {}; // assure que ceci n'est pas un module ambiant, sans quoi les types seraient écrasés

Modifier cette page sur Github

précédent suivant