Svelte与TypeScript #
一、TypeScript 配置 #
1.1 启用 TypeScript #
创建项目时选择 TypeScript:
bash
npm create svelte@latest my-app -- --template skeleton --types typescript
1.2 tsconfig.json #
json
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler",
"module": "ESNext",
"noImplicitAny": true,
"strictNullChecks": true,
"noUncheckedIndexedAccess": true
}
}
1.3 组件中使用 TypeScript #
svelte
<script lang="ts">
let count: number = 0;
let name: string = 'Svelte';
let items: string[] = [];
let user: { name: string; age: number } | null = null;
</script>
二、Props 类型定义 #
2.1 Svelte 5 Props 类型 #
svelte
<script lang="ts">
interface Props {
title: string;
count?: number;
items: string[];
onUpdate?: (value: number) => void;
}
let {
title,
count = 0,
items,
onUpdate
}: Props = $props();
</script>
<h1>{title}</h1>
<p>Count: {count}</p>
2.2 可选 Props #
svelte
<script lang="ts">
interface Props {
required: string;
optional?: number;
withDefault?: string;
}
let {
required,
optional,
withDefault = 'default value'
}: Props = $props();
</script>
2.3 复杂 Props 类型 #
svelte
<script lang="ts">
interface User {
id: string;
name: string;
email: string;
role: 'admin' | 'user' | 'guest';
}
interface Props {
user: User;
onUserUpdate: (user: User) => void;
onUserDelete: (id: string) => void;
}
let { user, onUserUpdate, onUserDelete }: Props = $props();
</script>
2.4 Snippet 类型 #
svelte
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
header?: Snippet;
footer?: Snippet;
children: Snippet;
}
let { header, footer, children }: Props = $props();
</script>
{#if header}
<header>{@render header()}</header>
{/if}
<main>{@render children()}</main>
{#if footer}
<footer>{@render footer()}</footer>
{/if}
2.5 带参数的 Snippet #
svelte
<script lang="ts">
import type { Snippet } from 'svelte';
interface Item {
id: string;
name: string;
}
interface Props {
items: Item[];
renderItem: Snippet<[Item, number]>;
}
let { items, renderItem }: Props = $props();
</script>
{#each items as item, index}
{@render renderItem(item, index)}
{/each}
三、响应式类型 #
3.1 $state 类型 #
svelte
<script lang="ts">
let count = $state<number>(0);
let name = $state<string>('Svelte');
let items = $state<string[]>([]);
let user = $state<User | null>(null);
</script>
3.2 $derived 类型 #
svelte
<script lang="ts">
let count = $state(0);
let doubled: number = $derived(count * 2);
let message: string = $derived(`Count is ${count}`);
let user = $state<User | null>(null);
let userName: string = $derived(user?.name ?? 'Guest');
</script>
3.3 $derived.by 类型 #
svelte
<script lang="ts">
interface Statistics {
total: number;
average: number;
max: number;
min: number;
}
let numbers = $state<number[]>([]);
let stats: Statistics = $derived.by(() => {
if (numbers.length === 0) {
return { total: 0, average: 0, max: 0, min: 0 };
}
const total = numbers.reduce((a, b) => a + b, 0);
return {
total,
average: total / numbers.length,
max: Math.max(...numbers),
min: Math.min(...numbers)
};
});
</script>
四、事件类型 #
4.1 DOM 事件类型 #
svelte
<script lang="ts">
function handleClick(event: MouseEvent): void {
console.log(event.clientX, event.clientY);
}
function handleKeyDown(event: KeyboardEvent): void {
if (event.key === 'Enter') {
console.log('Enter pressed');
}
}
function handleInput(event: Event): void {
const target = event.target as HTMLInputElement;
console.log(target.value);
}
</script>
<button onclick={handleClick}>Click</button>
<input onkeydown={handleKeyDown} oninput={handleInput} />
4.2 自定义事件类型 #
svelte
<script lang="ts">
interface CustomEventDetail {
id: string;
value: number;
}
function handleCustomEvent(event: CustomEvent<CustomEventDetail>): void {
console.log(event.detail.id, event.detail.value);
}
</script>
4.3 表单事件 #
svelte
<script lang="ts">
interface FormData {
email: string;
password: string;
remember: boolean;
}
function handleSubmit(event: SubmitEvent): void {
event.preventDefault();
const form = event.target as HTMLFormElement;
const formData = new FormData(form);
const data: FormData = {
email: formData.get('email') as string,
password: formData.get('password') as string,
remember: formData.get('remember') === 'on'
};
console.log(data);
}
</script>
<form onsubmit={handleSubmit}>
<input type="email" name="email" />
<input type="password" name="password" />
<input type="checkbox" name="remember" />
<button type="submit">Submit</button>
</form>
五、Store 类型 #
5.1 Writable Store 类型 #
typescript
import { writable, type Writable } from 'svelte/store';
interface User {
id: string;
name: string;
email: string;
}
export const user: Writable<User | null> = writable(null);
export const count: Writable<number> = writable(0);
5.2 Derived Store 类型 #
typescript
import { derived, type Readable } from 'svelte/store';
export const doubleCount: Readable<number> = derived(
count,
$count => $count * 2
);
export const userDisplayName: Readable<string> = derived(
user,
$user => $user?.name ?? 'Guest'
);
5.3 自定义 Store 类型 #
typescript
import { writable, type Readable } from 'svelte/store';
interface Todo {
id: string;
text: string;
done: boolean;
}
interface TodoStore extends Readable<Todo[]> {
add: (text: string) => void;
remove: (id: string) => void;
toggle: (id: string) => void;
}
function createTodoStore(): TodoStore {
const { subscribe, set, update } = writable<Todo[]>([]);
return {
subscribe,
add: (text: string) => {
update(todos => [...todos, { id: crypto.randomUUID(), text, done: false }]);
},
remove: (id: string) => {
update(todos => todos.filter(t => t.id !== id));
},
toggle: (id: string) => {
update(todos => todos.map(t =>
t.id === id ? { ...t, done: !t.done } : t
));
}
};
}
export const todos = createTodoStore();
六、Action 类型 #
6.1 Action 类型定义 #
typescript
import type { Action } from 'svelte/action';
interface TooltipOptions {
text: string;
position?: 'top' | 'bottom' | 'left' | 'right';
}
export const tooltip: Action<HTMLElement, TooltipOptions> = (
node,
options
) => {
// Implementation
return {
update(newOptions) {
// Update logic
},
destroy() {
// Cleanup
}
};
};
6.2 带事件的 Action #
typescript
import type { Action } from 'svelte/action';
interface LongpressOptions {
duration?: number;
}
interface LongpressAttributes {
'on:longpress'?: (event: CustomEvent) => void;
}
export const longpress: Action<
HTMLElement,
LongpressOptions,
LongpressAttributes
> = (node, { duration = 500 } = {}) => {
let timer: number;
function handleMouseDown() {
timer = setTimeout(() => {
node.dispatchEvent(new CustomEvent('longpress'));
}, duration);
}
function handleMouseUp() {
clearTimeout(timer);
}
node.addEventListener('mousedown', handleMouseDown);
node.addEventListener('mouseup', handleMouseUp);
node.addEventListener('mouseleave', handleMouseUp);
return {
update({ duration: newDuration = 500 }) {
duration = newDuration;
},
destroy() {
node.removeEventListener('mousedown', handleMouseDown);
node.removeEventListener('mouseup', handleMouseUp);
node.removeEventListener('mouseleave', handleMouseUp);
}
};
};
七、SvelteKit 类型 #
7.1 Page Data 类型 #
src/routes/blog/[slug]/+page.ts:
typescript
import type { PageLoad } from './$types';
interface Post {
id: string;
title: string;
content: string;
}
export const load: PageLoad = async ({ params, fetch }) => {
const response = await fetch(`/api/posts/${params.slug}`);
const post: Post = await response.json();
return { post };
};
src/routes/blog/[slug]/+page.svelte:
svelte
<script lang="ts">
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
</script>
<h1>{data.post.title}</h1>
<p>{data.post.content}</p>
7.2 Layout Data 类型 #
src/routes/+layout.ts:
typescript
import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async () => {
return {
siteName: 'My App'
};
};
src/routes/+layout.svelte:
svelte
<script lang="ts">
import type { LayoutData } from './$types';
let { data, children }: { data: LayoutData; children: import('svelte').Snippet } = $props();
</script>
<h1>{data.siteName}</h1>
{@render children()}
7.3 Server Load 类型 #
src/routes/+page.server.ts:
typescript
import type { PageServerLoad } from './$types';
import type { Actions } from './$types';
export const load: PageServerLoad = async ({ cookies }) => {
const session = cookies.get('session');
return { session };
};
export const actions: Actions = {
default: async ({ request, cookies }) => {
const formData = await request.formData();
return { success: true };
}
};
八、类型声明文件 #
8.1 全局类型声明 #
src/app.d.ts:
typescript
declare global {
namespace App {
interface Error {
code: string;
message: string;
}
interface Locals {
user: User | null;
sessionId: string | null;
}
interface PageData {
// ...
}
interface PageState {
// ...
}
interface Platform {
// ...
}
}
}
export {};
8.2 模块扩展 #
src/types/svelte.d.ts:
typescript
declare module '*.svelte' {
import type { SvelteComponent } from 'svelte';
export default SvelteComponent;
}
九、最佳实践 #
9.1 类型导入 #
typescript
// 推荐:使用 type 关键字
import type { User, Post } from '$lib/types';
import { formatDate } from '$lib/utils';
// 或使用内联类型导入
import { type User, type Post, formatDate } from '$lib';
9.2 严格空值检查 #
svelte
<script lang="ts">
let user = $state<User | null>(null);
// 使用可选链
let name = $derived(user?.name ?? 'Guest');
// 使用类型守卫
function getDisplayName(): string {
if (user === null) return 'Guest';
return user.name;
}
</script>
9.3 泛型组件 #
svelte
<script lang="ts" generics="T extends { id: string }">
interface Props {
items: T[];
onSelect: (item: T) => void;
renderItem: Snippet<[T]>;
}
let { items, onSelect, renderItem }: Props = $props();
</script>
{#each items as item (item.id)}
<button onclick={() => onSelect(item)}>
{@render renderItem(item)}
</button>
{/each}
十、总结 #
| 类型 | 说明 |
|---|---|
interface Props |
组件 Props 类型 |
$state<T>() |
响应式状态类型 |
$derived |
派生状态类型 |
PageData |
页面数据类型 |
LayoutData |
布局数据类型 |
Action |
Action 类型 |
TypeScript 要点:
- 使用
lang="ts"启用 TypeScript - 定义 Props 接口
- 使用类型安全的 Store
- 正确处理事件类型
- 利用 SvelteKit 自动生成的类型
- 启用严格模式
最后更新:2026-03-28