Home > front end >  Extract values from template literal types
Extract values from template literal types

Time:05-22

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.

Playground

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;
};
  • Related