メインコンテンツにスキップ

TypeScript

Svelteコンポーネント内でTypeScriptを使用できます。Svelte VS Code拡張機能のようなIDE拡張機能は、エディタでエラーをキャッチするのに役立ち、svelte-checkはコマンドラインで同じことを行います。これはCIに統合できます。

<script lang="ts">

Svelteコンポーネント内でTypeScriptを使用するには、scriptタグにlang="ts"を追加します。

<script lang="ts">
	let name: string = 'world';

	function greet(name: string) {
		alert(`Hello, ${name}!`);
	}
</script>

<button onclick={(e: Event) => greet(e.target.innerText)}>
	{name as string}
</button>

これにより、TypeScriptの_型のみ_の機能を使用できます。つまり、型注釈やインターフェース宣言など、JavaScriptにトランスパイルするときに単に消えるすべての機能です。TypeScriptコンパイラが実際のコードを出力する必要がある機能はサポートされていません。これには以下が含まれます。

  • 列挙型の使用
  • コンストラクタ関数でイニシャライザと共にprivateprotected、またはpublic修飾子を使用する
  • まだECMAScript標準の一部ではない(つまり、TC39プロセスではレベル4ではない)ため、JavaScriptの解析に使用しているパーサーであるAcorn内にまだ実装されていない機能を使用する

これらの機能のいずれかを使用する場合は、scriptプリプロセッサを設定する必要があります。

プリプロセッサの設定

Svelteコンポーネント内で型以外のTypeScript機能を使用するには、TypeScriptをJavaScriptに変換するプリプロセッサを追加する必要があります。

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

const 
const config: {
    preprocess: PreprocessorGroup;
}
config
= {
// Note the additional `{ script: true }` 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
;

SvelteKitまたはViteの使用

最も簡単な開始方法は、npx sv createと入力し、プロンプトに従ってTypeScriptオプションを選択して、新しいSvelteKitプロジェクトをスキャフォールディングすることです。

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
;

SvelteKitのすべての機能が必要ない、または必要としない場合は、npm create vite@latestと入力し、svelte-tsオプションを選択して、SvelteフレーバーのViteプロジェクトを代わりにスキャフォールディングできます。

どちらの場合も、vitePreprocessを使用したsvelte.config.jsが追加されます。Vite/SvelteKitはこの設定ファイルから読み取ります。

その他のビルドツール

代わりにRollupやWebpackなどのツールを使用している場合は、それぞれのSvelteプラグインをインストールしてください。Rollupの場合はrollup-plugin-svelte、Webpackの場合はsvelte-loaderです。両方とも、typescriptsvelte-preprocessをインストールし、プリプロセッサをプラグイン設定に追加する必要があります(詳細については、それぞれのREADMEを参照してください)。新しいプロジェクトを開始する場合は、rollupまたはwebpackテンプレートを使用して、スクリプトから設定をスキャフォールディングすることもできます。

新しいプロジェクトを開始する場合は、代わりにSvelteKitまたはViteを使用することをお勧めします。

tsconfig.json の設定

TypeScriptを使用する場合は、tsconfig.jsonが正しく設定されていることを確認してください。

  • targetを少なくともES2022、またはuseDefineForClassFieldsと合わせて少なくともES2015targetを使用してください。これは、クラスフィールドのルーン宣言が変更されないようにするため、Svelteコンパイラが壊れるのを防ぎます。
  • verbatimModuleSyntaxtrueに設定して、インポートがそのまま残されるようにします。
  • isolatedModulestrueに設定して、各ファイルが個別に参照されるようにします。TypeScriptには、ファイル間の分析とコンパイルが必要な機能がいくつかありますが、SvelteコンパイラやViteなどのツールはそれを行いません。

$props の型付け

$propsは、特定のプロパティを持つ通常のオブジェクトと同じように型付けします。

<script lang="ts">
	import type { Snippet } from 'svelte';

	interface Props {
		requiredProperty: number;
		optionalProperty?: boolean;
		snippetWithStringArgument: Snippet<[string]>;
		eventHandler: (arg: string) => void;
		[key: string]: unknown;
	}

	let {
		requiredProperty,
		optionalProperty,
		snippetWithStringArgument,
		eventHandler,
		...everythingElse
	}: Props = $props();
</script>

<button onclick={() => eventHandler('clicked button')}>
	{@render snippetWithStringArgument('hello')}
</button>

ジェネリック $props

コンポーネントは、プロパティ間のジェネリックな関係を宣言できます。1つの例は、項目のリストと、リストから項目を受け取るコールバックプロパティを受け取るジェネリックリストコンポーネントです。itemsプロパティとselectコールバックが同じ型で動作することを宣言するには、generics属性を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}

genericsの内容は、ジェネリック関数の<...>タグの間に配置するものです。つまり、複数のジェネリック、extends、およびフォールバック型を使用できます。

ラッパーコンポーネントの型付け

ネイティブ要素をラップするコンポーネントを作成している場合は、基になる要素のすべての属性をユーザーに公開したい場合があります。その場合は、svelte/elementsによって提供されるインターフェースの1つを使用(または拡張)します。Buttonコンポーネントの例を次に示します。

<script lang="ts">
	import type { HTMLButtonAttributes } from 'svelte/elements';

	let { children, ...rest }: HTMLButtonAttributes = $props();
</script>

<button {...rest}>
	{@render children?.()}
</button>

すべての要素に専用の型定義があるわけではありません。専用の型定義がない場合は、SvelteHTMLElementsを使用します。

<script lang="ts">
	import type { SvelteHTMLElements } from 'svelte/elements';

	let { children, ...rest }: SvelteHTMLElements['div'] = $props();
</script>

<div {...rest}>
	{@render children?.()}
</div>

$state の型付け

$stateは他の変数と同様に型付けできます。

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.dokyumento.jp/docs/svelte/$state

@paraminitial The initial value
$state
(0);

$stateに初期値を指定しないと、その型の一部は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.dokyumento.jp/docs/svelte/$state

@paraminitial The initial value
$state
();

変数が最初に使用される_前_に定義されることがわかっている場合は、asキャスティングを使用します。これは、クラスのコンテキストで特に役立ちます。

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.dokyumento.jp/docs/svelte/$state

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

Component 型

SvelteコンポーネントはComponent型です。これとその関連型を使用して、さまざまな制約を表現できます。

動的コンポーネントと一緒に使用して、渡すことができるコンポーネントの種類を制限する

<script lang="ts">
	import type { Component } from 'svelte';

	interface Props {
		// only components that have at most the "prop"
		// property required can be passed
		DynamicComponent: Component<{ prop: string }>;
	}

	let { DynamicComponent }: Props = $props();
</script>

<DynamicComponent prop="foo" />
レガシーモード

Svelte 4では、コンポーネントはSvelteComponent型でした。

コンポーネントからプロパティを抽出するには、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 MyComponent = SvelteComponent<Record<string, any>, any, any>
const MyComponent: LegacyComponentType
MyComponent
from './MyComponent.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>
) {} // Errors if the second argument is not the correct props expected // by the component in the first argument. function withProps<LegacyComponentType>(component: LegacyComponentType, props: Record<string, any>): voidwithProps(const MyComponent: LegacyComponentTypeMyComponent, { foo: stringfoo: 'bar' });

変数がコンポーネントのコンストラクタ型またはインスタンスタイプを予期していることを宣言するには

<script lang="ts">
	import MyComponent from './MyComponent.svelte';

	let componentConstructor: typeof MyComponent = MyComponent;
	let componentInstance: MyComponent;
</script>

<MyComponent bind:this={componentInstance} />

組み込み DOM 型の拡張

Svelteは、存在するすべてのHTML DOM型の最善の努力を提供します。アクションから来る実験的な属性またはカスタムイベントを使用したい場合があります。これらの場合、TypeScriptは型エラーをスローし、これらの型がわからないことを示します。実験的でない標準の属性/イベントである場合、これは私たちのHTML型定義からの型定義が不足している可能性が非常に高いです。その場合は、問題やPRを開いて修正することを歓迎します。

カスタムまたは実験的な属性/イベントの場合、次のように型定義を拡張できます。

additional-svelte-typings.d
declare namespace svelteHTML {
	// enhance elements
	interface interface svelteHTML.IntrinsicElementsIntrinsicElements {
		'my-custom-element': { someattribute: stringsomeattribute: string; 'on:event': (e: CustomEvent<any>e: interface CustomEvent<T = any>CustomEvent<any>) => void };
	}
	// enhance attributes
	interface interface svelteHTML.HTMLAttributes<T>HTMLAttributes<function (type parameter) T in HTMLAttributes<T>T> {
		// If you want to use the beforeinstallprompt event
		svelteHTML.HTMLAttributes<T>.onbeforeinstallprompt?: ((event: any) => any) | undefinedonbeforeinstallprompt?: (event: anyevent: any) => any;
		// If you want to use myCustomAttribute={..} (note: all lowercase)
		svelteHTML.HTMLAttributes<T>.mycustomattribute?: anymycustomattribute?: any; // You can replace any with something more specific if you like
	}
}

次に、d.tsファイルがtsconfig.jsonで参照されていることを確認します。"include": ["src/**/*"]のように読み取られ、d.tsファイルがsrc内にある場合は、動作するはずです。変更を有効にするには、リロードが必要になる場合があります。

次のようにsvelte/elementsモジュールを拡張することによって、型定義を宣言することもできます。

additional-svelte-typings.d
import { HTMLButtonAttributes } from 'svelte/elements';

declare module 'svelte/elements' {
	export interface SvelteHTMLElements {
		'custom-button': HTMLButtonAttributes;
	}

	// allows for more granular control over what element to add the typings to
	export interface HTMLButtonAttributes {
		HTMLButtonAttributes.veryexperimentalattribute?: string | undefinedveryexperimentalattribute?: string;
	}
}

export {}; // ensure this is not an ambient module, else types will be overridden instead of augmented

GitHubでこのページを編集する