Przejdź do treści
webtype.org#214 route-params · par 3

Nr 214 · 22 lipca 2026 · Średnia

Parametry trasy

Zaimplementuj `ExtractParams<Path>`, aby każdy segment `:param` ścieżki stał się kluczem typu obiektowego o wartości `string`. Wynik musi być jednym płaskim obiektem — przecięcie nie przejdzie.

01

Spróbuj rozwiązać samodzielnie

Par 3

Zagadka

route-params.ts
type Prettify<T> = { [K in keyof T]: T[K] } & {}
Podejście 1 z 3Jeszcze nie sprawdzone

Zastąp ??? — rozwiązanie sprawdzamy przypadkami poniżej.

Testy

4
  • ExtractParams<'/about'>
    {}
  • ExtractParams<'/users/:id'>
    { id: string }
  • ExtractParams<'/users/:id/posts/:postId'>
    { id: string; postId: string }
  • ExtractParams<'/:org/:repo/issues/:number'>
    { org: string; repo: string; number: string }

Jak oceniamy test Dokładna równość typów, nie przypisywalność — przecięcie to nie to samo co spłaszczony obiekt.

Jak poszło innym

Nobody is counting yet. Score distribution and the gra na krótkim dystansie board arrive with accounts — until then your results stay on this device.

Archiwum
02

Rozwiązanie z komentarzem

Opublikowano 23 lipca 2026

Rozwiązanie

type ExtractParams<Path extends string> = Prettify<
  Path extends `${string}:${infer Param}/${infer Rest}`
    ? { [K in Param]: string } & ExtractParams<`/${Rest}`>
    : Path extends `${string}:${infer Param}`
      ? { [K in Param]: string }
      : {}
>

Najczęstsza błędna odpowiedź

type ExtractParams<Path extends string> =
  Path extends `${string}:${infer Param}/${infer Rest}`
    ? { [K in Param]: string } & ExtractParams<`/${Rest}`>
    : Path extends `${string}:${infer Param}`
      ? { [K in Param]: string }
      : {}

Wszystko tu jest poprawne poza kształtem wyniku. Powstaje `{ id: string } & { postId: string }`, co da się przypisać do `{ id: string; postId: string }`, ale to nie ten sam typ — a testy sprawdzają dokładną równość. Owinięcie całości w `Prettify` spłaszcza przecięcie do jednego obiektu.

Linia po linii

  1. `${string}:${infer Param}/${infer Rest}`

    Wiodące `${string}` pochłania wszystko przed dwukropkiem, więc wzorzec nie przejmuje się tym, że ścieżka zaczyna się od `/users`. `Param` wiąże się aż do następnego ukośnika; `Rest` bierze całą resztę.

  2. ExtractParams<`/${Rest}`>

    Ukośnik jest dokładany z powrotem przed rekurencją. Bez niego końcowy segment w rodzaju `posts/:postId` wciąż pasowałby do pierwszego wzorca i rekurencja nie kończyłaby się czysto.

  3. Path extends `${string}:${infer Param}`

    Drugi wzorzec nie ma końcowego ukośnika, więc pasuje tylko wtedy, gdy parametr jest ostatnim elementem łańcucha. To przypadek bazowy zatrzymujący rekurencję.

  4. Prettify<...>

    Mapuje raz jeszcze po zebranych kluczach, tworząc świeży typ obiektowy. Końcówka `& {}` w `Prettify` zmusza TypeScript do faktycznego wyliczenia typu mapowanego zamiast trzymania go leniwie.

Do zapamiętania

Wzorce literałów szablonowych dopasowują się zachłannie od lewej. Gdy zbierasz typy obiektowe przez rekurencję, dostajesz przecięcie, a testy dokładnej równości je odrzucą — spłaszcz je przed zwróceniem.