typescript 避免模板文字类型中的重复

bttbmeg0  于 2023-04-13  发布在  TypeScript
关注(0)|答案(1)|浏览(157)

我声明了一个从url中提取参数的类型:

type ExtractParams<Path extends string> =
  Path extends `${infer Start}/:${infer Param}/${infer Rest}`
    ? ExtractParams<Start> & ExtractParams<Rest> & { [Key in Param]: string; }
    : Path extends `${infer Start}/:${infer Param}`
      ? ExtractParams<Start> & { [Key in Param]: string }
      : {}

代码运行良好:

type X1 = ExtractParams<'/courses/:courseId/classes/:classId'>
//   ^? { classId: string; } & { courseId: string; }

这里是Playground Link
但这里有两件事可以改进:
1.避免重复
如果你检查ExtractParams类型定义,你可以看到我使用了嵌套条件。外部条件查找/:/之间的参数。内部条件查找字符串末尾的参数。所以,我想把/${infer Rest}声明为可选的,但是我不知道如何在模板文字类型中声明一些可选的东西。你能帮我解释一下吗?
2.合并交点
现在输出看起来很难看,因为每个参数都有自己的对象。有没有办法合并这些对象?

更新:

从以下答案中得到了合并交叉点的答案:https://stackoverflow.com/a/58965725/2284240
为了在我的代码中合并交叉点,我可以像这样更新它:

type Expand<T> = T extends infer U ? { [K in keyof U]: U[K] } : never;

type Params<Path extends string> = Expand<ExtractParams<Path>>;

type X1 = Params<'/courses/:courseId/classes/:classId'>
//   ^? { classId: string; courseId: string }

更新Playground Link
不把它作为答案贴出来,因为,我还在寻找一个解决方案避免重复

yvfmudvl

yvfmudvl1#

我推荐的方法是用"/"个字符将路径分割成一个路径段的并集:

type PathSegments<T extends string, A extends string = never> =
  T extends `${infer L}/${infer R}` ? PathSegments<R, A | L> : A | T;

这是一个尾递归条件类型,并为您的示例生成如下内容:

type PS1 = PathSegments<'/courses/:courseId/classes/:classId'>
// type PS1 = "" | "courses" | ":courseId" | "classes" | ":classId"

然后你可以只使用那些以:字符开始的段作为你的输出类型的键。

type Params<T extends string> = {
  [K in PathSegments<T> as K extends `:${infer P}` ? P : never]:
  string };

使用键重Map来禁止任何不以:开始的联合成员。对于生成

type X1 = Params<'/courses/:courseId/classes/:classId'>
/* type X1 = {
    courseId: string;
    classId: string;
} */

看起来是你想要的
Playground链接到代码

相关问题