跳到主要內容

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 編譯器輸出實際程式碼的功能則不支援。這包括

  • 使用列舉
  • 在建構函式中搭配初始設定式使用 privateprotectedpublic 修飾詞
  • 使用尚未成為 ECMAScript 標準一部分(亦即在 TC39 流程中尚未達到第 4 級)的功能,因此 Acorn 中(我們用於剖析 JavaScript 的剖析器)尚未實作

如果您想要使用其中一個功能,您需要設定 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 來建立新的 SvelteKit 專案,依照提示並選擇 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
;

如果您不需要或不想要 SvelteKit 提供的所有功能,您也可以輸入 npm create vite@latest 並選取 svelte-ts 選項,來建立一個使用 Svelte 的 Vite 專案。

在這兩種情況下,都會加入一個包含 vitePreprocesssvelte.config.js。Vite/SvelteKit 將會從這個設定檔讀取。

其他建置工具

如果您改用 Rollup 或 Webpack 等工具,請安裝它們各自的 Svelte 外掛程式。Rollup 的外掛程式是 rollup-plugin-svelte,而 Webpack 的外掛程式是 svelte-loader。對於這兩個外掛程式,您都需要安裝 typescriptsvelte-preprocess,並將預處理器加入到外掛程式設定中(如需更多資訊,請參閱各自的 README)。如果您要開始新的專案,您也可以使用 rollupwebpack 範本從指令碼建立設定。

如果您要開始新的專案,我們建議您改用 SvelteKit 或 Vite。

tsconfig.json 設定

使用 TypeScript 時,請確保您的 tsconfig.json 已正確設定。

  • 使用至少 ES2022target,或至少 ES2015target 搭配 useDefineForClassFields。這可確保類別欄位上的符文宣告不會遭到干擾,否則會破壞 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

元件可以宣告其屬性之間的泛型關係。其中一個範例是一個泛型清單元件,該元件接收一個項目清單和一個回呼屬性,該屬性接收清單中的一個項目。若要宣告 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 提供的其中一個介面。以下是 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.dev.org.tw/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.dev.org.tw/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.dev.org.tw/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
	}
}

然後確保您的 tsconfig.json 中參考了 d.ts 檔案。如果它讀取類似 "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 上編輯此頁面

上一頁 下一頁