Suppose I have types like this:
type SegmentBase = string;
type ParamSegment = `:${string}`;
type Segment = SegmentBase | ParamSegment;
type Path = `${Segment}/${Segment}`;
Is it now possible to construct a type Extractor<T extends Path>
that extracts the ${string}
part of a ParamSegment
in the following way:
Extractor<'foo/:bar'>
// turns into
{
bar: string;
}
Extractor<':foo/:bar'>
// turns into
{
foo: string;
bar: string;
}
Extractor<'foo/bar'>
// turns into
{}
CodePudding user response:
I suppose you need something like this:
type Extractor<S extends string> =
S extends `${infer L}/${infer R}`
? {
[K in L | R as K extends `:${infer Key}` ? Key : never]: string
}
: never
Some test cases:
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 = {}
Please add more test cases, if this does not fit your use case.
CodePudding user response:
You need to create a recursive auxiliary type for this.
First, create a type that matches the parameter names
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];
Then you can do
export type Extractor<T extends string> = {
[K in Match<T>]: string;
};