0Pricing
TypeScript Academy · 课时

设计流式查询 DSL

构建可链式调用且能自我验证的查询 API。

设计流式查询 DSL 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。

设计流式查询 DSL

我们要设计一种可链式调用的查询 DSL,通过返回类型让每一步细化接下来允许执行的步骤。最终结果读起来像结构化查询语言,并会在编译时拒绝无效顺序。

目标语法

我们的目标是:先调用from,然后是可选且可重复的where,最后是终止步骤select。您不能在from之前调用select,也不能调用两次from。

状态接口

将每个阶段建模为一个返回下一阶段的接口。

interface Builder {
  from(table: string): FromStage;
}
interface FromStage {
  where(cond: string): FromStage; // repeatable
  select(...cols: string[]): Result;
}
interface Result { sql: string; }

强制执行顺序

由于只有FromStage上存在select,在初始的Builder上调用它会产生编译错误。顺序完全由每个阶段公开的方法来强制执行。

declare const db: Builder;
db.from("users").select("id"); // ok
db.select("id");               // Error: select missing on Builder

跟踪已选择的列

添加一个幽灵泛型来记住已选择的列,从而使结果类型更加精确。

interface FromStage<T extends string = never> {
  where(c: string): FromStage<T>;
  select<C extends string>(...cols: C[]): Result<C>;
}
interface Result<C extends string> { columns: C[]; }

随着每次调用进行细化

每个where也可以在类型中累积约束。这里我们保持简单,但这种模式可以推广到跟踪已绑定的参数。

const q = db.from("users").where("age > 18").where("active = true");
// still FromStage; select remains available

防止重复调用 from

由于FromStage没有公开from,您无法调用它两次。语法在结构上禁止了这种操作,不需要运行时保护。

db.from("a").from("b"); // Error: from does not exist on FromStage

终止步骤

select返回Result,它既不公开where,也不公开from,因此终止了调用链。之后只剩下读取结果的操作。

const r = db.from("users").select("id", "name");
r.columns; // ("id" | "name")[]
// r.where(...) -> Error: where not on Result

类型化的列约束

使用另一个泛型将列约束为已知的表结构,这样未知列就会被拒绝,也能让此 DSL 与前面介绍的对象关系映射思想结合起来。

interface Table<Cols extends string> {
  select<C extends Cols>(...cols: C[]): Result<C>;
}
// db.from gives Table<"id" | "name" | "age">

可选步骤与必需步骤

只公开后续方法,即可将某个步骤设为必需步骤。例如,返回一个只有在调用where之后才出现select的阶段,从而强制至少调用一次where。同样的链式类型还可以推断执行查询后的结果行类型,将编译时语法与运行时数据联系起来。

为何重要

按照这种方式设计的流式 DSL 能够自我说明,并且无法被误用:自动补全只会显示有效的后续步骤,非法顺序永远无法通过编译。这是易用型构建器库的基础。

快速检查

确认您对流式 DSL 设计的理解。

回顾

您将流式查询 DSL 设计为一个类型层面的状态机:每个阶段接口都会返回下一阶段,并且只公开有效的方法。幽灵泛型用于跟踪已选择的列,终止阶段用于结束调用链,列约束用于拒绝未知名称,而这一切都由返回类型强制执行。

常见问题解答

「设计流式查询 DSL」课时是免费的吗?

是的 — 「设计流式查询 DSL」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。

「设计流式查询 DSL」这节课中我会学到什么?

构建可链式调用且能自我验证的查询 API。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 TypeScript Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「设计流式查询 DSL」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 TypeScript Academy 课中编写并运行代码吗?

能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 什么是类型级 DSL
  2. 设计流式查询 DSL
  3. 编译时输入验证
  4. 类型级 DSL 中的错误消息
← 返回 TypeScript Academy