typescript将/path/:param转换为/path/string的类型

需求是要用指定的字符串限制 url 的输入,比如限制 url 只能输入'/user'或者/home,然后想到了之后可能会用动态的 param,'/user/:userId'这种

express 里可以使用app.post("/user/:userId", ...)这样,在下面用req.params来获取userId.

但是在前端发送请求的时候,需要填像axios.get({url: "/user/123"})这样,所以写了一个类型来声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type AllowedPath = "/user/:userId" | "/user"; // 路径

/** 检查字符串是否为空 */
type IsEmptyString<S extends string> = S extends "" ? true : false;

/** 将带/:的路径,转换为'/路径/字符串' */
type Path<P extends AllowedPath> = P extends `/${infer MainPath}/:${string}`
? `/${MainPath}/${string}`
: P;

/** 检查第二个/后面的字符串是否为空,如果为空就返回never */
type Param<S extends string> = S extends `/${infer MainPath}/${infer Param}`
? IsEmptyString<Param> extends true
? never
: `/${MainPath}/${Param}`
: S;

function path<P extends AllowedPath, T extends Path<P> = Path<P>>(
path: Param<T>
) {
console.log(path);
}

path("/user"); // valid
path("/user/123"); // valid
path("/user/"); // 报错,类型“"/user/"”的参数不能赋给类型“never”的参数。

踩坑的一个点是

1
2
3
4
// 刚开始写成这样,发现ts判断不了,导致写 /user/ 也不会报错,需要把Path<P>拆分到泛型参数里才行
function path<P extends AllowedPath>(path: Param<Path<P>>) {
console.log(path);
}

目前只匹配到第二个斜杠 ‘/‘ ,不知道之后有没有更多 param 斜杠的需求。暂时没想到多个 param 的时候有没有更好的写法,应该可以用多个三元判断,或者把拆分 :/ 做成工具函数,然后递归一下。之后遇到了再补充