Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recursive solution for Transform Path Parameters from Strings to Objects #15

Open
jbalsas opened this issue Apr 14, 2023 · 2 comments
Open

Comments

@jbalsas
Copy link

jbalsas commented Apr 14, 2023

Hi @mattpocock!!

First of all, thanks so much for these materials you put together. I don't think I've become a wizard yet, but going through the topics and exercises has been quite fun and I feel like I've learned a lot from it already!! 🤯 😍

I just finished watching Transform Path Parameters from Strings to Objects. I didn't think to use the S.Split helper, so I came up with a recursive solution that felt easier to reason with (for me that I wrote it, of course! 😂)

Just pasting it here in case you think something like that might help others in the future and curious to know how you think it compares with the proposed solution ❤️

import { Equal, Expect } from '../helpers/type-utils';

type UserPath = '/users/:id';
type UserOrganisationPath = '/users/:id/organisations/:organisationId';
type UserOrganisationTeamPath =
  '/users/:id/organisations/:organisationId/teams/:teamId';

// Extracts param by param until the end 
type ExtractParams<T> = T extends `${string}:${infer Param}/${infer RestParams}`
  ? Param | ExtractParams<RestParams>
  : T extends `${string}:${infer Param}`
  ? Param
  : never;

type ExtractPathParams<T> = {
  [K in ExtractParams<T>]: string;
};

type tests = [
  Expect<Equal<ExtractPathParams<UserPath>, { id: string }>>,
  Expect<
    Equal<
      ExtractPathParams<UserOrganisationPath>,
      { id: string; organisationId: string }
    >
  >,
  Expect<
    Equal<
      ExtractPathParams<UserOrganisationTeamPath>,
      { id: string; organisationId: string; teamId: string }
    >
  >
];
@Viijay-Kr
Copy link

Viijay-Kr commented Apr 21, 2023

@jbalsas I like the solution. I too feel its easier to reason with. I struggled with the Split solution twice in a row.

I definitely like the recursive approach better.

type Url = "/home?search=hello&count=100";

type QueryParams<T> = T extends `${string}?${infer QueryParams}`
  ? QueryParams
  : never;

type ExtractQuery<T> =
  T extends `${infer Param}=${infer Value}&${infer RestOfQueries}`
    ? Param | ExtractQuery<RestOfQueries>
    : T extends `${infer Param}=${infer Value}`
    ? Param
    : never;

type ExtractQueryParams<T> = {
  [K in ExtractQuery<T>]: string;
};

type SearchQueryObject = ExtractQueryParams<QueryParams<Url>>;

Nicely works for extracting query params too

@jbalsas
Copy link
Author

jbalsas commented Apr 24, 2023

Nice!

I think Split is obviously sort of a more refined version of this recursive approach. I gave the utility approach a try and came up with this, without the key remapping which I guess is sort of the thing that immediately clicked for me but now it's present when I think about this.

import { Equal, Expect } from '../helpers/type-utils';

type UserPath = '/users/:id';

type UserOrganisationPath = 'a/users/:id/organisations/:organisationId';

type UserOrganisationTeamPath =
  '/users/:id/organisations/:organisationId/teams/:teamId';

type Split<
  T extends string,
  Sep extends string
> = T extends `${infer Param}${Sep}${infer Rest}`
  ? `${Param}` | Split<Rest, Sep>
  : `${T}`;

type Trim<
  T extends string,
  Char extends string
> = T extends `${Char}${infer Rest}` ? Rest : T;

type StartsWith<
  T extends string,
  Char extends string
> = T extends `${Char}${string}` ? T : never;

type ExtractPathParams<T extends string> = {
  [K in Trim<StartsWith<Split<T, '/'>, ':'>, ':'>]: string;
};

type tests = [
  Expect<Equal<ExtractPathParams<UserPath>, { id: string }>>,
  Expect<
    Equal<
      ExtractPathParams<UserOrganisationPath>,
      { id: string; organisationId: string }
    >
  >,
  Expect<
    Equal<
      ExtractPathParams<UserOrganisationTeamPath>,
      { id: string; organisationId: string; teamId: string }
    >
  >
];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants