可能是全网第一个分析的人,如果能帮助你,请点个关注呗!😁
一、写在前面
一开始我也觉得这个功能简直
泰裤辣
!完全不需要自己重新定义一个类型就可以自动检测,后面发现实现起来并不难。这个功能主要使用类型推导出来的,分别为:去尾、获取参数key、分配到对象,这三个步骤。下面简单说一下:
二、具体三部曲
①去尾
// 去尾类型
type RemoveTail<S extends string, Tail extends string> = S extends `${infer P}${Tail}` ? P : S;
以上类型你可以理解为把重要的内容
P
和无用的尾部分离出来,并且去掉Tail
无用部分。
以下为简单分析事例
// 去尾类型
type RemoveTail<S extends string, Tail extends string> =
S extends `${infer P}${Tail}` ? P : S;
// 模拟一个字符串
const c = 'aaaa/abc/:id'
// 结果类型
type newD = RemoveTail<typeof c, `/${string}`>
// 此时newD为aaaa
②获取对应参数
// 获取对应参数
type GetRouteParameter<S extends string> = RemoveTail<
RemoveTail<RemoveTail<S, `/${string}`>, `-${string}`>,
`.${string}`
>;
以上类型你很清晰看到他会把
/xxx
、-xxx
、.xxx
此类部分全部删掉。那怎么叫做获取参数呢?因为他要配合extends
和infer
使用
以下为具体示例
// 去尾类型
type RemoveTail<S extends string, Tail extends string> =
S extends `${infer P}${Tail}` ? P : S;
// 获取对应参数
type GetRouteParameter<S extends string> = RemoveTail<
RemoveTail<RemoveTail<S, `/${string}`>, `-${string}`>,
`.${string}`
>;
// 模拟一个字符串
const c = 'aaaa/abc/:id'
// 真正获取对应参数
type RealParamter<T> = T extends `${string}:${infer Rest}`
? GetRouteParameter<Rest>
: T;
type resType = RealParamter<typeof c>
// 此时计算出来的resType 为id
是不是很开心,这时候你已经完成了一半了😊😊😊
但是问题又来了,你会发现,你添加c添加多一个:user_id
也会被忽略,加要看③步骤了
③分配到对象
// 修改第二步事例代码,我们可以通过递归的方式,把第一个参数后面的参数都输出
type RealParamter<T> = T extends `${string}:${infer Rest}`
? (GetRouteParameter<Rest> extends never
? {}
: { [P in GetRouteParameter<Rest>]: string })
& (Rest extends `${GetRouteParameter<Rest>}${infer Next}`
? RealParamter<Next>
: unknown)
: {};
type resType = RealParamter<typeof c>
// 此时输出 { id: string } & { user_id: string }
收工了老铁,官方源码位置
整个事例
// 去尾类型
type RemoveTail<S extends string, Tail extends string> =
S extends `${infer P}${Tail}` ? P : S;
// 获取对应参数
type GetRouteParameter<S extends string> = RemoveTail<
RemoveTail<RemoveTail<S, `/${string}`>, `-${string}`>,
`.${string}`
>;
interface ParamsDictionary {
[key: string]: string;
}
// 模拟一个字符串
const c = 'aaaa/abc/:id/:user_id'
// 真正获取对应参数
type RealParamter<T extends string> = T extends `${string}:${infer Rest}`
? (GetRouteParameter<Rest> extends never
? ParamsDictionary
: { [P in GetRouteParameter<Rest>]: string })
& (Rest extends `${GetRouteParameter<Rest>}${infer Next}`
? RealParamter<Next>
: unknown)
: {};
interface TestFunc {
<
Route extends string,
>(path: Route) : RealParamter<typeof path>
}
const b: TestFunc = (path) => {
const obj: ParamsDictionary = {}
path.replace(/\/\:([^\/]+)/g, ($0, $1) => {
if (typeof $1 === 'string') {
obj[$1] = '';
}
return ''
})
return (obj as RealParamter<typeof path>)
}
console.log(b(c).id)