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コンパイラが実際のコードを出力する必要がある機能はサポートされていません。これには以下が含まれます。
- 列挙型の使用
- コンストラクタ関数でイニシャライザと共に
private
、protected
、またはpublic
修飾子を使用する - まだECMAScript標準の一部ではない(つまり、TC39プロセスではレベル4ではない)ため、JavaScriptの解析に使用しているパーサーであるAcorn内にまだ実装されていない機能を使用する
これらの機能のいずれかを使用する場合は、script
プリプロセッサを設定する必要があります。
プリプロセッサの設定
Svelteコンポーネント内で型以外のTypeScript機能を使用するには、TypeScriptをJavaScriptに変換するプリプロセッサを追加する必要があります。
import { function vitePreprocess(opts?: VitePreprocessOptions | undefined): import("svelte/compiler").PreprocessorGroup
vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const const config: {
preprocess: PreprocessorGroup;
}
config = {
// Note the additional `{ script: true }`
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;
SvelteKitまたはViteの使用
最も簡単な開始方法は、npx sv create
と入力し、プロンプトに従ってTypeScriptオプションを選択して、新しいSvelteKitプロジェクトをスキャフォールディングすることです。
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;
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です。両方とも、typescript
とsvelte-preprocess
をインストールし、プリプロセッサをプラグイン設定に追加する必要があります(詳細については、それぞれのREADMEを参照してください)。新しいプロジェクトを開始する場合は、rollupまたはwebpackテンプレートを使用して、スクリプトから設定をスキャフォールディングすることもできます。
新しいプロジェクトを開始する場合は、代わりにSvelteKitまたはViteを使用することをお勧めします。
tsconfig.json の設定
TypeScriptを使用する場合は、tsconfig.json
が正しく設定されていることを確認してください。
target
を少なくともES2022
、またはuseDefineForClassFields
と合わせて少なくともES2015
のtarget
を使用してください。これは、クラスフィールドのルーン宣言が変更されないようにするため、Svelteコンパイラが壊れるのを防ぎます。verbatimModuleSyntax
をtrue
に設定して、インポートがそのまま残されるようにします。isolatedModules
をtrue
に設定して、各ファイルが個別に参照されるようにします。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: number
count: number = function $state<0>(initial: 0): 0 (+1 overload)
namespace $state
Declares reactive state.
Example:
let count = $state(0);
$state(0);
$state
に初期値を指定しないと、その型の一部は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
Declares reactive state.
Example:
let count = $state(0);
$state();
変数が最初に使用される_前_に定義されることがわかっている場合は、as
キャスティングを使用します。これは、クラスのコンテキストで特に役立ちます。
class class Counter
Counter {
Counter.count: number
count = function $state<number>(): number | undefined (+1 overload)
namespace $state
Declares reactive state.
Example:
let count = $state(0);
$state() as number;
constructor(initial: number
initial: number) {
this.Counter.count: number
count = initial: number
initial;
}
}
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<{ 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 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>): 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>
) {}
// 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>): void
withProps(const MyComponent: LegacyComponentType
MyComponent, { foo: string
foo: '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を開いて修正することを歓迎します。
カスタムまたは実験的な属性/イベントの場合、次のように型定義を拡張できます。
declare namespace svelteHTML {
// enhance elements
interface interface svelteHTML.IntrinsicElements
IntrinsicElements {
'my-custom-element': { someattribute: string
someattribute: 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) | 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
}
}
次に、d.ts
ファイルがtsconfig.json
で参照されていることを確認します。"include": ["src/**/*"]
のように読み取られ、d.ts
ファイルがsrc
内にある場合は、動作するはずです。変更を有効にするには、リロードが必要になる場合があります。
次のようにsvelte/elements
モジュールを拡張することによって、型定義を宣言することもできます。
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 | undefined
veryexperimentalattribute?: string;
}
}
export {}; // ensure this is not an ambient module, else types will be overridden instead of augmented