Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract values from template literal types

Tags:

typescript

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
{}
like image 757
Lehks Avatar asked Oct 23 '25 03:10

Lehks


1 Answers

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

Edit: Since Typescript 4.5, it's better to implement the _Match type as bellow, so it can benefit from 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>;
like image 142
Diego Fidalgo Avatar answered Oct 25 '25 02:10

Diego Fidalgo