Skip to main content

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(`你好,${name}!`);
	}
</script>

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

这样做可以让您使用 TypeScript 的 仅使用类型(type-only) 功能。

所谓“仅使用类型”功能,也就是所有在转译为 JavaScript 时会消失的功能,例如类型注解或接口声明。需要 TypeScript 编译器输出实际代码的功能则不被支持,包括:

  • 使用枚举(enums)
  • 在构造函数中与初始化器(initializers)一起使用 privateprotectedpublic 修饰符
  • 使用尚未成为 ECMAScript 标准的功能(即未达到 TC39 流程的第 4 阶段),因为没有到达最后的阶段,所以 Svelte 用于解析 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
= {
// 注意额外的 `{ 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('点击按钮')}>
	{@render snippetWithStringArgument('你好')}
</button>

泛型 $props

组件可以声明其属性之间的泛型关系。下面的示例是一个通用列表组件,接收一个条目列表和一个回调属性,该回调接收列表中的一个条目。为了声明 items 属性和 select 回调对同一类型进行操作,请在 script 标记中添加 generics 属性:

<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 和后备类型。

包裹组件的类型定义

如果您编写的组件是一个包裹原生元素的组件,您可能想要将底层元素的所有属性暴露给用户。在这种情况下,请使用(或 extend)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/docs/svelte/$state

@paraminitial The initial value
$state
(0);

如果您没有给 $state 提供初始值,它的部分类型将是 undefined

// 错误:类型 'number | undefined' 不可分配给类型 '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
();

如果您知道这个变量 将在 第一次使用之前被定义,请使用 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/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 {
		// 仅允许传递至多需要 "prop"
		// 属性的组件
		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>
) {} // 如果第二个参数不是第一个参数的组件所期望的正确属性,则会产生错误。 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 类型。但有时,您可能希望使用实验性属性或来自 action 的自定义事件。在这些情况下,TypeScript 会抛出类型错误,表示它不知道这些类型。如果是非实验性标准属性/事件,很可能是我们 HTML 类型定义 中缺少了一些类型。在这种情况下,欢迎您提起一个 issue 和(或)提交一个修复的 PR。

如果这是一个自定义或实验性属性/事件,您可以像这样增强类型定义:

additional-svelte-typings.d
declare namespace svelteHTML {
	// 增强元素
	interface interface svelteHTML.IntrinsicElementsIntrinsicElements {
		'my-custom-element': { someattribute: stringsomeattribute: string; 'on:event': (e: CustomEvent<any>e: interface CustomEvent<T = any>CustomEvent<any>) => void };
	}
	// 增强属性
	interface interface svelteHTML.HTMLAttributes<T>HTMLAttributes<function (type parameter) T in HTMLAttributes<T>T> {
		// 如果您想使用 beforeinstallprompt 事件
		svelteHTML.HTMLAttributes<T>.onbeforeinstallprompt?: ((event: any) => any) | undefinedonbeforeinstallprompt?: (event: anyevent: any) => any;
		// 如果您想使用 myCustomAttribute={..} (NOTE: 全小写)
		svelteHTML.HTMLAttributes<T>.mycustomattribute?: anymycustomattribute?: any; // 如果需要,您可以将 any 替换为更具体的类型
	}
}

然后确保在您的 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;
	}

	// 允许对要添加类型定义的元素进行更细粒度的控制
	export interface HTMLButtonAttributes {
		HTMLButtonAttributes.veryexperimentalattribute?: string | undefinedveryexperimentalattribute?: string;
	}
}

export {}; // 确保这不是一个环境模块,否则类型会被覆盖而不是增强

在 GitHub 编辑此页面

上一页 下一页