Wasp TypeScript 支持完全指南:JS 项目渐进式迁移与端到端类型安全实战
Wasp TypeScript 支持完全指南JS 项目渐进式迁移与端到端类型安全实战【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/waspTypeScript 在 Wasp 中是开箱即用的——无论是新建项目直接使用 TS还是将既有 JavaScript 项目逐文件渐进式迁移都不需要额外安装或配置。本篇指南以 Wasp 官方文档为主体结合本仓库wasp中真实生成器模板与配置源码讲解wasp/entities、wasp/server/operations等 Wasp 自动生成的类型系统如何工作并通过一个Task查询的完整迁移示例带你掌握从.js到.ts的每一步操作。为什么选择 TypeScript静态类型分析的价值TypeScript 是一门为 JavaScript 增加静态类型分析能力的编程语言。它是 JavaScript 的超集——所有合法的 JavaScript 代码都是合法的 TypeScript 代码——并且会在运行前编译为 JavaScript。它的类型系统带来两个核心收益在构建期捕获错误类型错误在编译阶段而非运行时被暴露显著降低线上运行时错误率基于类型的 IDE 自动补全编辑器可以根据类型信息提供智能提示IntelliSense提升开发效率。在 Wasp 中每个功能模块都配套有对应的 TypeScript 文档例如数据模型entities、查询queries与操作actions均有独立的类型化说明。这意味着类型安全不是 Wasp 的附加项而是贯穿整个框架的核心能力。新建项目使用 TypeScript 无需任何特殊操作如果你是从零开始一个新项目完全不需要做任何额外的事直接按照你感兴趣的功能文档操作即可相关文档会告诉你需要知道的一切。官方建议新手先从 Wasp 官方教程 入手逐步熟悉 Wasp 的声明式开发流程。而如果你手上有一个已经用 JavaScript 写好的 Wasp 项目想迁移到 TypeScript则请继续阅读下面的迁移指南。迁移原理改扩展名 写类型Wasp 对 TypeScript 的支持是出厂自带out-of-the-box的因此迁移一个项目本质上只有两件事更改文件扩展名.js→.ts编写类型并可选地使用 Wasp 提供的 TypeScript 特性。这种设计允许你按文件逐个渐进式迁移整个项目无需一次性推倒重来。下面先演示如何迁移一个文件再把流程推广到整个项目。迁移单个文件一个 Task 查询的完整实战第 1 步确认数据模型与 Wasp 声明假设你的schema.prisma文件中定义了Task实体// ... model Task { id Int id default(autoincrement()) description String isDone Boolean }同时main.wasp文件中声明了名为getTaskInfo的查询Query它从src/queries导入实现函数并关联Task实体query getTaskInfo { fn: import { getTaskInfo } from src/queries, entities: [Task] }第 2 步迁移前的 JavaScript 实现下面是待迁移的src/queries.js文件。它定义了一个内部辅助函数getInfoMessage以及导出给 Wasp 使用的getTaskInfo查询实现import HttpError from wasp/server function getInfoMessage(task) { const isDoneText task.isDone ? is done : is not done return Task ${task.description} is ${isDoneText}. } export const getTaskInfo async ({ id }, context) { const Task context.entities.Task const task await Task.findUnique({ where: { id } }) if (!task) { throw new HttpError(404) } return getInfoMessage(task) }注意这里有两个明显的类型盲区getInfoMessage的参数task没有任何类型约束getTaskInfo的args与context也完全依赖开发者记忆——这正是我们要用 TypeScript 修复的。第 3 步迁移到 TypeScript迁移只需要两步把文件名从queries.js改为queries.ts编写类型并可选地启用 Wasp 的 TypeScript 特性。对照如下——迁移前Before与迁移后Afterimport HttpError from wasp/core/HttpError.js function getInfoMessage(task) { const isDoneText task.isDone ? is done : is not done return Task ${task.description} is ${isDoneText}. } export const getTaskInfo async ({ id }, context) { const Task context.entities.Task const task await Task.findUnique({ where: { id } }) if (!task) { throw new HttpError(404) } return getInfoMessage(task) }import HttpError from wasp/server import { type Task } from wasp/entities import { type GetTaskInfo } from wasp/server/operations function getInfoMessage(task: PickTask, isDone | description): string { const isDoneText task.isDone ? is done : is not done return Task ${task.description} is ${isDoneText}. } export const getTaskInfo: GetTaskInfoPickTask, id, string async ( { id }, context ) { const Task context.entities.Task const task await Task.findUnique({ where: { id } }) if (!task) { throw new HttpError(404) } return getInfoMessage(task) }迁移后你的代码由 TypeScript 接管并使用了两个 Wasp 特有的类型特性Task类型连接 Prisma 数据模型import { type Task } from wasp/entitiesTask是表示Task实体的类型它的来源是schema.prisma中的模型定义。使用这一类型你的业务代码就与数据库模型建立了强类型连接当 Prisma 模型字段变化时所有引用处的类型检查会立刻提示你。在示例中我们通过PickTask, isDone | description只挑选辅助函数真正用到的两个字段既精确又避免了与整个实体类型的过度耦合。关于实体类型的更多用法可参考 数据模型与实体文档。GetTaskInfo泛型Wasp 自动生成的查询类型import { type GetTaskInfo } from wasp/server/operationsGetTaskInfo...是 Wasp 为每个在main.wasp中声明的查询自动生成的泛型类型。给实现函数标注上它之后编译器会自动获知context对象的类型包括context.entities.Task对应的 Prisma delegate 类型args参数的类型查询的返回类型。于是编辑器会为你提供 IntelliSense 与完整的类型检查。在示例中GetTaskInfoPickTask, id, string表示该查询接收一个包含id字段的参数对象返回一个string。关于查询实现类型的完整说明见 查询Queries文档。注意整个迁移过程不需要改动.wasp文件在 version-0.16 中为main.waspWasp 的声明层对文件扩展名是透明的。迁移整个项目逐文件渐进式三步流程你可以按文件粒度渐进式迁移整个项目每迁移一个文件都遵循上面演示的流程更改文件扩展名.js→.ts修复类型错误让tsc编译通过阅读对应功能的 Wasp 文档决定要启用哪些 TypeScript 特性如wasp/entities实体类型、wasp/server/operations的操作泛型等。这种渐进式策略允许 JavaScript 与 TypeScript 文件在同一个 Wasp 项目中长期共存团队可以按模块优先级分批推进无需大爆炸式重写。深入原理这些类型从哪来上面用到的wasp/entities与wasp/server/operations并非手写代码而是 Wasp 生成器在每次wasp start/wasp build时自动生成的 SDK 的一部分。在本仓库中可以找到它们的生成模板实体类型从 Prisma Client 直接复用模板 waspc/data/Generator/templates/sdk/wasp/entities/index.ts 展示了wasp/entities的生成逻辑Wasp 直接把prisma/client中生成的实体类型重新导出并同时导出Entity实体联合类型与EntityName实体名字符串联合类型import type { {# entities } { name }, {/ entities } } from prisma/client export type { {# entities } { name }, {/ entities } } from prisma/client export type Entity {# entities }| { name }{/ entities }| never export type EntityName {# entities }| { name }{/ entities }| never这就是为什么import { type Task } from wasp/entities能与你schema.prisma中的model Task保持完全同步——它本质上是 Prisma Client 类型的一份转发。操作泛型查询/操作类型的生成骨架模板 waspc/data/Generator/templates/sdk/wasp/server/operations/queries/types.ts 定义了每个查询类型如GetTaskInfo的生成骨架它是一个接收Input, Output两个泛型参数的别名内部根据该查询是否启用认证解析为AuthenticatedQueryDefinition或UnauthenticatedQueryDefinitionexport type { typeName }Input extends Payload never, Output extends Payload Payload {# usesAuth } AuthenticatedQueryDefinition {/ usesAuth } {^ usesAuth } UnauthenticatedQueryDefinition {/ usesAuth } [ {# entities }{ internalTypeName },{/ entities } ], Input, Output 而上下文context的类型由模板 waspc/data/Generator/templates/sdk/wasp/server/_types/index.ts 定义ContextEntities会展开为包含entities字段的对象entities的类型是EntityMap——由实体名 → Prisma delegate的映射推导而来启用认证时则会扩展出带user字段的ContextWithUser。查询实现如何被注入 entities你写的查询函数并不会被直接调用。生成器模板 waspc/data/Generator/templates/server/src/queries/_query.ts 显示Wasp 会为每个查询生成一个包装函数在调用你的实现前注入entities将prisma.Task等 delegate 挂到context.entities上export default async function (args, context) { return ({ jsFn.importIdentifier } as any)(args, { ...context, entities: { {# entities } { name }: prisma.{ prismaIdentifier }, {/ entities } }, }) }这解释了为何你可以在getTaskInfo中直接使用context.entities.Task.findUnique(...)也解释了为什么GetTaskInfo泛型能够精确推断context的类型——两者同源于你在 Wasp 声明中列出的entities。构建期的强制类型检查Wasp 不仅在编辑器层面提供类型还在构建期强制检查。生成器中的 Vite 插件模板 waspc/data/Generator/templates/sdk/wasp/client/vite/plugins/typescriptCheck.ts 会在buildStart阶段调用tsc --project srcTsConfig --noEmit任何类型错误都会导致构建失败TypeScript check failedconst child spawn( tsc, [--project, srcTsConfigPath, --noEmit], { stdio: inherit, shell: process.platform win32 } )换句话说类型安全从编辑器的建议升级为构建的硬性门槛。项目中的 TypeScript 配置参考仓库内的真实 Wasp 项目展示了标准的 TS 配置。例如 examples/kitchen-sink/tsconfig.wasp.json 开启了strict模式、moduleResolution: bundler、allowJs允许 JS/TS 共存正是渐进式迁移的配置基础并将**/*.wasp.ts与.wasp/out/types/spec纳入检查范围各示例项目根目录下的tsconfig.json、tsconfig.src.json则分别面向应用源码与构建产物可直接作为自己项目配置的参照。常见坑编辑器 LSP 报错与解决方案在开发过程中你可能会遇到一个典型现象wasp start正常运行但编辑器却报告类型错误或导入错误。这通常是因为 TypeScript Language ServerTS 语言服务器与当前代码状态不同步——Wasp 在启动时会动态生成wasp/entities、wasp/server/operations等 SDK 类型如果语言服务器缓存了旧版本的生成文件就会产生幽灵报错。如果你使用 VS Code可以通过命令面板手动重启 TS 语言服务器来恢复同步Windows / Linux按CtrlShiftP打开命令面板macOS按CmdShiftP打开命令面板然后选择TypeScript: Restart TS Server即可。小结Wasp 的 TypeScript 支持贯穿编写—检查—构建全流程新建项目零配置即可使用存量 JS 项目可通过改扩展名 写类型的方式逐文件渐进迁移wasp/entities与wasp/server/operations等自动生成的类型让实体数据与查询实现获得端到端的类型安全构建期的tsc检查则把类型错误拦截在发布之前。掌握本文的迁移流程与类型机制你就可以放心地把任意 Wasp 项目平滑升级到全量 TypeScript。进一步阅读Wasp 官方教程从创建项目开始数据模型与实体entities查询queries实现与类型【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考