TypeScript Academy · 课时

小型类型级路由解析器

将路由路径解析为带类型的参数对象。

第 4 / 4 课13 个步骤

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

目标

我们要构建一个路由解析器,读取类似"users/:id/posts/:postId"的路径,并生成类型化的参数对象{ id: string; postId: string },整个过程都在编译时完成。这是类型化路由器中实际使用的一种模式。

type Params = ParseRoute<"users/:id/posts/:postId">;
// Goal: { id: string; postId: string }

识别参数

当路径片段以":"开头时,它就是一个参数。我们使用模板字面量匹配此前缀,并推断冒号后的参数名称。

表示法:真实的 TypeScript 会使用由反引号包围、包含美元符号加大括号占位符的字符串来书写模板字面量类型。在这些代码片段中,我们将这种模式表示为 Tpl<...>,并按顺序列出每个部分;例如,匹配字面量前缀后跟 Rest 的反引号模板表示为 Tpl<'prefix', infer Rest>。

type ParamName<S> = S extends Tpl<':', infer Name> ? Name : never;
// Tpl<...> denotes a backtick template literal type: a colon then Name

type X = ParamName<':id'>;   // 'id'
type Y = ParamName<'users'>; // never

拆分路径

首先复用Split类型,按"/"将路由拆分成多个片段。随后,每个片段要么是字面量,要么是参数。

type Split<S extends string, Sep extends string> =
  S extends Tpl<infer H, Sep, infer T> ? [H, ...Split<T, Sep>] : [S];
// Tpl<H, Sep, T> = a template literal type matching H, then Sep, then T

type Segs = Split<'users/:id', '/'>; // ['users', ':id']

直接进行模板递归

我们也可以不先拆分,而是通过一次匹配一个片段来解析原始字符串。匹配到下一个"/"之前的内容,处理该片段,然后对剩余部分递归处理。

type ParseRoute<S extends string> =
  S extends Tpl<infer Seg, '/', infer Rest>
    ? SegParam<Seg> & ParseRoute<Rest>
    : SegParam<S>;
// Tpl<Seg, '/', Rest> matches a segment, a slash, then the rest

逐片段处理参数

这个辅助类型会将单个片段转换为单属性对象(如果它是参数)或空对象(如果它是字面量)。

type SegParam<S extends string> =
  S extends Tpl<':', infer Name> ? { [K in Name]: string } : {};
// Tpl<':', Name> matches a colon followed by the param name

type X = SegParam<':id'>;   // { id: string }
type Y = SegParam<'posts'>; // {}

使用交叉类型组合

每个片段都通过交叉类型贡献自己的参数。空对象在交叉类型中会消失,因此最终类型中只保留真正的参数。

type Params = ParseRoute<"users/:id/posts/:postId">;
// { id: string } & {} & { postId: string } & {}
// = { id: string; postId: string }

跟踪解析过程

对于 "users/:id/posts/:postId":

  • 片段 "users" -> {},对 ":id/posts/:postId" 递归处理
  • 片段 ":id" -> { id: string },对 "posts/:postId" 递归处理
  • 片段 "posts" -> {},对 ":postId" 递归处理
  • 片段 ":postId" -> { postId: string },基本情况
type Final = { id: string } & { postId: string };

清理结果

对象的交叉类型显示起来不太直观。一个“美化”辅助类型会使用针对其键的映射类型,将它展平为单个整洁的对象类型。

type Prettify<T> = { [K in keyof T]: T[K] } & {};

type Clean = Prettify<ParseRoute<"users/:id">>; // { id: string }

使用解析器

将解析出的类型接入函数,使处理函数能够接收类型正确的参数。传入错误的键会成为编译错误。

declare function route<P extends string>(
  path: P,
  handler: (params: Prettify<ParseRoute<P>>) => void
): void;

route("users/:id", p => { const id: string = p.id; });

类型化参数的优势

现在,编译器会直接从路由字符串中推导出params。如果重命名路径中的参数,每个处理函数所需的键都会自动更新。不需要手动编写接口,也不会出现路由与处理函数不一致的情况。

route("users/:userId", p => {
  const u = p.userId; // ok
  // const x = p.id; // error: id does not exist
});

扩展解析器

这个小型解析器是完整类型化路由器的雏形。您可以添加类型化值(数字参数)、通配符和可选片段,所有这些都可以通过为逐片段辅助类型添加更多模板模式来实现。递归模板结构保持不变。

type SegParam2<S extends string> =
  S extends Tpl<':', infer N, '(number)'> ? { [K in N]: number }
  : S extends Tpl<':', infer N> ? { [K in N]: string }
  : {};
// Tpl<...> stands for a backtick template literal type pattern

快速检查

测试您对路由解析器的理解。

回顾

您已经构建了一个编译时路由解析器。

  • 使用递归模板字面量推断匹配每个片段。
  • 参数片段(:name)会变成{ name: string }。
  • 字面量片段会变成{},并在交叉类型中消失。
  • Prettify会将结果展平为整洁的参数。

下一步是第 25 课:使用 tRPC 实现端到端类型安全。

免费开始

用 AI 导师学习 TypeScript — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
101
课程
352

常见问题解答

「小型类型级路由解析器」课时是免费的吗?

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

「小型类型级路由解析器」这节课中我会学到什么?

将路由路径解析为带类型的参数对象。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 TypeScript Academy 需要有经验吗?

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

「小型类型级路由解析器」课时需要多长时间?

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

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

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

此课程中的所有课时

  1. 解析器组合子概念
  2. 类型级字符串拆分
  3. 使用模板字面量进行解析
  4. 小型类型级路由解析器
← 返回 TypeScript Academy