TypeScript
您可以在 Svelte 组件中使用 TypeScript。像 Svelte VS Code 扩展 这样的 IDE 扩展将帮助您在编辑器中捕获错误,而 svelte-check
在命令行中执行相同的操作,您可以将其集成到您的 CI 中。
<script lang="ts">
要在 Svelte 组件内使用 TypeScript,请将 lang="ts"
添加到您的 script
标签中
<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
来搭建一个新的 SvelteKit 项目,按照提示操作并选择 TypeScript 选项。
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 提供的所有功能,您可以改为搭建一个 Svelte 风格的 Vite 项目,方法是键入 npm create vite@latest
并选择 svelte-ts
选项。
在这两种情况下,都会添加一个带有 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
设置正确。
- 使用至少为
ES2022
的target
,或与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
组件可以在其属性之间声明泛型关系。一个示例是接收项目列表和接收来自列表的项目的回调属性的泛型列表组件。要声明 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: number
count: number = function $state<0>(initial: 0): 0 (+1 overload)
namespace $state
$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
$state();
如果您知道变量将在首次使用之前定义,请使用 as
转换。这在类的上下文中特别有用
class class Counter
Counter {
Counter.count: number
count = function $state<number>(): number | undefined (+1 overload)
namespace $state
$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
}
}
然后确保在您的 tsconfig.json
中引用 d.ts
文件。如果它读取类似 "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