Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Svelte 最新中文文档教程(20)—— TypeScript #348

Open
mqyqingfeng opened this issue Feb 26, 2025 · 0 comments
Open

Svelte 最新中文文档教程(20)—— TypeScript #348

mqyqingfeng opened this issue Feb 26, 2025 · 0 comments

Comments

@mqyqingfeng
Copy link
Owner

前言

Svelte,一个语法简洁、入门容易,面向未来的前端框架。从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1

image.png

Svelte 以其独特的编译时优化机制著称,具有轻量级高性能易上手等特性,非常适合构建轻量级 Web 项目,也是我做个人项目的首选技术栈。

目前 Svelte 基于 Svelte 5 发布了最新的官方文档,但却缺少对应的中文文档。为了帮助大家学习 Svelte,为爱发电翻译了官方文档。

我同时搭建了 Svelte 最新的中文文档站点:https://svelte.yayujs.com ,如果需要辅助学习,也可以入手我的小册《Svelte 开发指南》,语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!

虽说是翻译,但个人并不喜欢严格遵守原文,为了保证中文阅读流畅,会删减部分语句,对难懂的部分也会另做补充解释,希望能给大家带来一个好的中文学习体验。

欢迎围观我的“网页版朋友圈”、加入“冴羽·成长陪伴社群”,踏上“前端大佬成长之路”

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。

/// file: svelte.config.js
// @noErrors
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

const config = {
	// 注意额外的 `{ script: true }`
	preprocess: vitePreprocess({ script: true })
};

export default config;

使用 SvelteKit 或 Vite

最简单的开始方式是输入 npx sv create 搭建一个新的 SvelteKit 项目,然后按照提示选择 TypeScript 选项。

/// file: svelte.config.js
// @noErrors
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

const config = {
	preprocess: vitePreprocess()
};

export default 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 模板搭建设置环境。

[!NOTE] 如果您要开启一个新项目,建议使用 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 count: number = $state(0);

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

// @noErrors
// 错误:类型 'number | undefined' 不可分配给类型 'number'
let count: number = $state();

如果您知道这个变量 将在 第一次使用之前被定义,请使用 as 强制类型转换。这在类的上下文中特别有用:

class Counter {
	count = $state() as number;
	constructor(initial: number) {
		this.count = initial;
	}
}

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 { Component, ComponentProps } from 'svelte';
import MyComponent from './MyComponent.svelte';

function withProps<TComponent extends Component<any>>(
	component: TComponent,
	props: ComponentProps<TComponent>
) {}

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

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

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

然后确保在您的 tsconfig.json 中引用了 d.ts 文件。如果它的内容类似于 "include": ["src/**/*"] 并且您的 d.ts 文件位于 src 目录中,它应该可以正常工作。您可能需要重新加载以使更改生效。

您还可以通过增强 svelte/elements 模块来声明类型,如下所示:

/// file: additional-svelte-typings.d.ts
import { HTMLButtonAttributes } from 'svelte/elements';

declare module 'svelte/elements' {
	export interface SvelteHTMLElements {
		'custom-button': HTMLButtonAttributes;
	}

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

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

Svelte 中文文档

本篇已收录在掘金专栏 《Svelte 中文文档》,该系列预计 40 篇。

系统学习 Svelte,欢迎入手小册《Svelte 开发指南》。语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!

此外我还写过 JavaScript 系列TypeScript 系列React 系列Next.js 系列冴羽答读者问等 14 个系列文章, 全系列文章目录:https://github.com/mqyqingfeng/Blog

欢迎围观我的“网页版朋友圈”、加入“冴羽·成长陪伴社群”,踏上“前端大佬成长之路”

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant