从模板文字类型中提取值
Extract values from template literal types
假设我有这样的类型:
type SegmentBase = string;
type ParamSegment = `:${string}`;
type Segment = SegmentBase | ParamSegment;
type Path = `${Segment}/${Segment}`;
现在是否可以构造一个类型 Extractor<T extends Path>
以下列方式提取 ParamSegment
的 ${string}
部分:
Extractor<'foo/:bar'>
// turns into
{
bar: string;
}
Extractor<':foo/:bar'>
// turns into
{
foo: string;
bar: string;
}
Extractor<'foo/bar'>
// turns into
{}
我想你需要这样的东西:
type Extractor<S extends string> =
S extends `${infer L}/${infer R}`
? {
[K in L | R as K extends `:${infer Key}` ? Key : never]: string
}
: never
部分测试用例:
type T1 = Extractor<'foo/:bar'>
// type T1 = {
// bar: string;
// }
type T2 = Extractor<':foo/:bar'>
// type T2 = {
// foo: string;
// bar: string;
// }
type T3 = Extractor<'foo/bar'>
// type T3 = {}
如果这不适合您的用例,请添加更多测试用例。
您需要为此创建一个递归辅助类型。
首先,创建一个匹配参数名称的类型
type _UnwrapParam<P extends string, S extends string[]> = P extends `:${infer Q}` ? [Q, ...S] : S;
type _Match<T extends string, S extends string[]> = T extends `${infer P}/${infer R}`
? [...UnwrapParam<P, S>, ..._Match<R, S>]
: _UnwrapParam<T, S>
export type Match<T extends string> = _Match<T, []>[number];
那你可以做
export type Extractor<T extends string> = {
[K in Match<T>]: string;
};
编辑: 从 Typescript 4.5 开始,最好按如下方式实现 _Match
类型,这样它可以受益于 Tail recursion elimination.
type _Match<
T extends string,
S extends string[]
> = T extends `${infer P}/${infer R}`
? _Match<R, _UnwrapParam<P, S>>
: _UnwrapParam<T, S>;
假设我有这样的类型:
type SegmentBase = string;
type ParamSegment = `:${string}`;
type Segment = SegmentBase | ParamSegment;
type Path = `${Segment}/${Segment}`;
现在是否可以构造一个类型 Extractor<T extends Path>
以下列方式提取 ParamSegment
的 ${string}
部分:
Extractor<'foo/:bar'>
// turns into
{
bar: string;
}
Extractor<':foo/:bar'>
// turns into
{
foo: string;
bar: string;
}
Extractor<'foo/bar'>
// turns into
{}
我想你需要这样的东西:
type Extractor<S extends string> =
S extends `${infer L}/${infer R}`
? {
[K in L | R as K extends `:${infer Key}` ? Key : never]: string
}
: never
部分测试用例:
type T1 = Extractor<'foo/:bar'>
// type T1 = {
// bar: string;
// }
type T2 = Extractor<':foo/:bar'>
// type T2 = {
// foo: string;
// bar: string;
// }
type T3 = Extractor<'foo/bar'>
// type T3 = {}
如果这不适合您的用例,请添加更多测试用例。
您需要为此创建一个递归辅助类型。
首先,创建一个匹配参数名称的类型
type _UnwrapParam<P extends string, S extends string[]> = P extends `:${infer Q}` ? [Q, ...S] : S;
type _Match<T extends string, S extends string[]> = T extends `${infer P}/${infer R}`
? [...UnwrapParam<P, S>, ..._Match<R, S>]
: _UnwrapParam<T, S>
export type Match<T extends string> = _Match<T, []>[number];
那你可以做
export type Extractor<T extends string> = {
[K in Match<T>]: string;
};
编辑: 从 Typescript 4.5 开始,最好按如下方式实现 _Match
类型,这样它可以受益于 Tail recursion elimination.
type _Match<
T extends string,
S extends string[]
> = T extends `${infer P}/${infer R}`
? _Match<R, _UnwrapParam<P, S>>
: _UnwrapParam<T, S>;