# 9616-ParseUrlParams
# 题目描述
实现一个类型层面的 URL 参数解析器,从给定的 URL 字符串里抽取出所有以 : 开头的参数名,以联合类型的形式返回。
type A = ParseUrlParams<':id'>; // 'id'
type B = ParseUrlParams<'posts/:id'>; // 'id'
type C = ParseUrlParams<'posts/:id/:user'>; // 'id' | 'user'
# 分析
思路拆成两步:
- 遍历整个字符串,遇到
:,就把紧跟着的那一段"到下一个/或字符串末尾"的子串提取出来。 - 提取到的每一段拼成联合。
字符串遍历用模板匹配 + 递归是体操的经典套路(参考 类型转换大集合)。关键是"吃到冒号后"的处理:再来一次模板匹配,把冒号后直到 / 的部分捞出来。
# 题解
type ParseUrlParams<U> = U extends `${infer L}:${infer P}`
? P extends `${infer Param}/${infer R}`
? Param | ParseUrlParams<R>
: P
: never;
- 第一层
${infer L}:${infer P}:把字符串在第一个:处切开,L是冒号前的部分(丢弃),P是冒号后的部分。 - 第二层
${infer Param}/${infer R}:检查冒号后是否还有/。- 有:
Param就是"冒号到下一个/"之间的参数名,继续对R递归。 - 没有:说明冒号后到字符串结尾都是参数名,直接返回
P。
- 有:
- 如果连一个
:都没有,返回never,递归出口。
# 验证
type R1 = ParseUrlParams<':id'>; // 'id'
type R2 = ParseUrlParams<'posts/:id'>; // 'id'
type R3 = ParseUrlParams<'posts/:id/:user'>; // 'id' | 'user'
type R4 = ParseUrlParams<'a/b/c'>; // never,没有参数