Wasp 语言(.wasp)详解:用声明式领域特定语言描述全栈 Web 应用
2026/9/14 18:41:04 网站建设 项目流程

Wasp 语言(.wasp)详解:用声明式领域特定语言描述全栈 Web 应用

【免费下载链接】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

本篇基于 Wasp 官方文档(version-0.13 快照)中的《Wasp Language (.wasp)》一文,系统讲解 Wasp 语言的声明语法、完整类型系统与领域类型清单,并结合 waspc 编译器源码(Haskell 实现的 Analyzer 类型系统)逐条印证每个语言特性的真实定义,帮助你从“会写 .wasp 文件”深入到“理解 Wasp 编译器如何解析、类型检查你的声明”。

什么是 Wasp 语言

Wasp 语言(即你在.wasp文件中编写的代码)是一种声明式、静态类型的领域特定语言(DSL)。它不是通用编程语言,而更接近一种配置语言——语法上更接近 JSON、CSS 或 SQL,而非 JavaScript 或 Python。这意味着它没有控制流、没有函数定义,你只需要“声明”应用的各个组成部分,剩下的全栈行为(路由、认证、数据库、后台任务等)由 Wasp 编译器生成和接管。

Wasp 官方文档的态度很务实:这门语言足够直观,简单到你可以不读这篇语言规范、而是在学习其他功能文档时顺带掌握它;但如果你想获得更形式化的定义和对内部工作机制的更深理解,就需要理解下面两个核心:声明(declarations)类型系统

声明(Declarations):Wasp 代码的唯一组成单元

Wasp 语言的中心概念是声明——Wasp 代码在本质上就是一组声明的集合,每个声明描述你 Web 应用的一个部分。文档给出的典型示例:

app MyApp { title: "My app" } route RootRoute { path: "/", to: DashboardPage } page DashboardPage { component: import { DashboardPage } from "@src/Dashboard.jsx" }

这个示例用三个声明描述了一个 Web 应用:app MyApp { ... }route RootRoute { ... }page DashboardPage { ... }

声明的统一写法为:

<declaration_type> <declaration_name> <declaration_body>

三个组成部分的含义:

  • <declaration_type>:Wasp 提供的声明类型之一(approutepage等,完整清单见下文领域类型一节);
  • <declaration_name>:你为该声明选定的标识符(如MyApp,也可以叫foobarfoo_barhi3Ho);
  • <declaration_body>:声明本身的值/定义,其类型必须符合所选声明类型期望的声明体类型(declaration body type)。

app声明为例拆解:

  • 声明类型:app
  • 声明名:MyApp
  • 声明体:{ title: "My app" }——一个包含字符串值字段title的字典(dict),该字典类型与app声明类型的声明体类型一致。如果你把title改成little,Wasp 编译器会给出类型错误,因为它不符合app声明体的期望类型。

每个声明都有背后明确的语义,描述你的 Web 应用应如何表现与运作。而 Wasp 语言中的其余所有类型——基本类型(stringnumber)、复合类型(dictlist)、枚举类型(DbSystem)等——全部用于定义声明体

完整类型清单:Fundamental 类型与 Domain 类型

Wasp 的类型系统分为两大类:

  • Fundamental 类型(基础类型):语言的基本构建块,与其他主流语言中的概念相似;
  • Domain 类型(领域类型):这是 Wasp 的独特之处——它们对 Web 应用的概念建模,如pageroute等。

基本类型(Primitive types)

类型字面量示例说明
string"foo""they said: \"hi\""字符串
booltruefalse布尔值
number1214.5数字(整数或小数)
declaration referenceTaskPageupdateTask对已存在声明名称的引用
ExtImportimport Foo from "@src/bar.js"import { Smth } from "@src/a/b.js"外部导入,引用src目录下的 JS/TS 模块
json{=json { a: 5, b: ["hi"] } json=}通过引号器(quoter)内嵌 JSON
psl{=psl <psl data model syntax> psl=}通过引号器内嵌 Prisma Schema Language 数据模型语法

其中两个值得展开的要点:

ExtImport 的约束规则

  • 路径必须以@src开头,其余部分相对于src目录解析;
  • 导入只允许两种形式:默认导入import Foo或单个具名导入import { Foo }

引号器(Quoter)语法{=<tag> ... <tag>=}形式允许在声明体中内嵌其他语法的文本片段,目前支持jsonpsl两种 tag;psl用于直接嵌入 Prisma 的数据模型定义(其语法以 Prisma 官方文档为准)。

复合类型(Composite types)

类型字面量示例说明
dict(字典){ a: 5, b: "foo" }键值对集合,字段可必填或可选
list(列表)[1, 2, 3]同元素类型的有序集合
tuple(元组)(1, "bar")(2, 4, true)定长混合类型,只允许大小为 2、3、4

领域类型(Domain types)

这是 Wasp 区别于普通配置格式的关键,全部用于描述 Web 应用概念:

声明类型(declaration types)

  • action——服务端可被页面调用的操作
  • api——暴露给前端的 HTTP 接口
  • apiNamespace——api 的命名空间分组
  • app——应用根声明(标题、认证、数据库、部署等配置都挂在这里)
  • entity——数据模型实体(与schema.prisma联动)
  • job——后台任务
  • page——页面(绑定 React 组件与路由目标)
  • query——服务端只读查询
  • route——URL 路由
  • crud——为实体一键生成增删改查能力

枚举类型(enum types)

  • DbSystem——数据库系统(如 Postgres、Sqlite)
  • HttpMethod——HTTP 方法
  • JobExecutor——任务执行器
  • EmailProvider——邮件发送提供方

每种领域类型的声明体类型与业务含义,在 Wasp 文档对应功能章节中逐一展开(如 认证 各声明的 body 说明)。

源码印证:类型系统如何落地在 waspc 编译器中

以上类型清单并非纸面定义,它们与 waspc(Wasp 编译器)Haskell 源码一一对应。原文档标注的“source of truth”在当前仓库中即以下两个文件。

类型 ADT:所有 Wasp 类型的形式化定义

waspc/src/Wasp/Analyzer/Type.hs 定义了全部可能类型的代数数据类型:

data Type = DeclType String -- 声明类型,如 app、page | EnumType String -- 枚举类型,如 HttpMethod | DictType (H.HashMap String DictEntryType) -- 字典,字段可必填/可选 | ListType Type | EmptyListType -- 空列表的临时类型 | TupleType (Type, Type, [Type]) -- 元组:固定前两个 + 可选剩余 | StringType | NumberType | BoolType | ExtImportType | QuoterType String -- {=tag ... tag=} 引号器

几处源码细节直接印证了文档描述:

  • 元组大小限制TupleType (Type, Type, [Type])的构造形态恰好对应文档中“元组只允许大小为 2、3、4”的规则——前两个元素固定存在,第三个之后的剩余类型通过[Type]列表表达,至多再容纳两个;
  • 字典字段的必填/可选区分:DictEntryType 分为DictRequiredDictOptional两种,这解释了为何app声明体必须提供titleauthdb等字段可以省略——app声明体中不同字段的强制性正是由这一结构决定的;
  • EmptyListType是内部机制:源码注释说明空列表在类型检查期间被赋予临时类型,待typeCheck完成后统一替换为具体ListType,属于编译器内部细节,对用户透明。

领域类型清单:stdTypes 注入机制

waspc/src/Wasp/Analyzer/StdTypeDefinitions.hs 通过stdTypes集中注册全部标准领域类型:

stdTypes :: TD.TypeDefinitions stdTypes = TD.addDeclType @App $ TD.addEnumType @DeploymentMode $ TD.addDeclType @Entity $ TD.addDeclType @Page $ TD.addDeclType @Route $ TD.addDeclType @Query $ TD.addDeclType @Action $ TD.addEnumType @JobExecutor $ TD.addDeclType @Job $ TD.addEnumType @HttpMethod $ TD.addDeclType @Api $ TD.addDeclType @ApiNamespace $ TD.addEnumType @EmailProvider $ TD.addDeclType @Crud $ TD.empty

源码注释明确说明:这些领域类型是**以注入方式(而非硬编码进 Analyzer)**提供给编译器的,目的是让修改和维护 Wasp 语言本身更容易。对照文档的类型清单,两者的声明类型(actionapiapiNamespaceappentityjobpagequeryroutecrud)与枚举类型完全吻合。从源码结构看,当前主干还多出一个未在 version-0.13 文档枚举中列出的DeploymentMode枚举(用于app声明中的部署配置),这体现了文档快照与主干演进之间的正常差异——阅读本文结论时应以文档快照对应的 Wasp 版本行为为准。

声明体的真实字段:以apppage为例

文档提到app声明体是“一个包含title字段(字符串值)的字典”。而编译器侧的App记录(waspc/src/Wasp/AppSpec/App.hs)完整呈现了该声明体在类型检查通过后的形态:

data App = App { wasp :: Wasp, -- 声明自身的元数据(名字、源位置) title :: String, -- 必填,对应文档示例中的 { title: "My app" } deployment :: Maybe Deployment, head :: Maybe [String], auth :: Maybe Auth, server :: Maybe Server, client :: Maybe Client, db :: Maybe Db, emailSender :: Maybe EmailSender, webSocket :: Maybe WebSocket }

title :: String是必填项,其余字段均为Maybe——与Type.hsDictRequired/DictOptional的区分精确对应。这也解释了文档中“把title改成little会报类型错误”的原因:titleapp字典类型中唯一的必填字段。

同理,waspc/src/Wasp/AppSpec/Page.hs 表明page声明体只有两个字段:

data Page = Page { component :: ExtImport, -- 必填,且必须是 ExtImport 类型 authRequired :: Maybe Bool }

component字段类型是ExtImport——这从类型层面印证了 ExtImport 的独立地位:它不是普通 string,而是专门用于引用src目录下 JS/TS 模块的原始类型,编译器据此校验@src前缀与“默认导入或单个具名导入”两条规则。

声明的编译流水线:从 .wasp 文本到类型检查

Wasp 声明从文本到语义的完整处理链路,在 Analyzer 模块说明 中有一张流水线图(即本文开头的 Analyzer 图):.wasp文件先由 Parser 解析为 AST,再进入类型检查器完成声明体类型校验。README 特别指出了一个容易忽略的环节:在 Parser 与 TypeChecker 之间,还有一个基于解析后的schema.prisma文件注入entity声明的步骤——也就是说,entity类声明并非直接写在.wasp文件里,而是由编译器从 Prisma schema 派生并注入,这也是psl引号器类型存在的意义所在。

小结与延伸阅读

  • 掌握<declaration_type> <declaration_name> <declaration_body>三要素、基本类型(含 ExtImport 与 json/psl 引号器)以及领域类型清单,就覆盖了 Wasp 语言的完整语法面;
  • 语言规范以官方文档为准,本文引用的版本快照位于 web/versioned_docs/version-0.13/general/language.md,各类型的“source of truth”源码位于 waspc/src/Wasp/Analyzer/Type.hs 与 waspc/src/Wasp/Analyzer/StdTypeDefinitions.hs;
  • 每种声明类型(action、query、api、job、crud 等)的声明体字段与行为语义,建议在 Wasp 文档对应功能章节中结合本文的类型系统视角继续阅读。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询