2

My goal is to have a function that takes a key and an object with query parameters and the function can look up a URL, interpolate with the query params and navigate to that. What I got so far is this

  interface FooIdParams {
    fooId: number;
  }

  interface QueryParams<P = never> {
    params: P;
  }

  enum Urls {
    'foo-editor' = '/editfoo?fooId=${fooId}'
  }

  interface ParamTypes {
    'foo-editor': QueryParams<FooIdParams>
  }

and the load method in my class looks like this:

  load = <T extends keyof ParamTypes>(target: T, params: ParamTypes[T]["params"]):void => {
    const url = eval('`' + Urls[target] + '`')
    
    // alternative with replace like suggested in a comment
    // this also works with multiple query params
    const url2 = Urls[target].replaceAll(/\$\{(.*?)\}/g, (_: string, k: string) => (params as any)[k])

    // and some code to open that page
  }

This already works and ensures that the called provides correct query params (later I have dozens of URL targets and the load method is called from many places, so it's worth the effort)

The part that I can't solve: I use the key foo-editor in two different places, the enum where it maps to the 'url' and in the interface, where it maps to the type. And I'd like to guarantee, that both enum and interface have exactly the same keys (like - typescript should force to mappings in both and prevent typos).

Is there a way to ensure that?

5
  • 1
    Instead of eval I'd really use .replace(/\$\{(.*)\}/g, k => params[k]); Commented Mar 3, 2021 at 9:48
  • I try - but it's not that easy, because I now have to find the correct type for k - typescript complains. Commented Mar 3, 2021 at 10:12
  • probably one of the cases were any simplifies things (playground) Commented Mar 3, 2021 at 10:14
  • Yes, acceptable here - I have type safety already on the method, so params is guaranteed to be of the correct type and will have all required keys. Thanks! Commented Mar 3, 2021 at 10:18
  • @JonasWilms - I've switched to your suggestion but your solution had two bugs: first, k in your code is the full match - second one is the captured value that we want. And then, it only works with one param, because the regexp is greedy by default. I edit my question and put the working alternative in it. Thanks for at least pointing me into that direction! Commented Mar 3, 2021 at 12:02

1 Answer 1

3

You could replace the enum with a mapped type:

 const Urls: { [K in keyof ParamTypes]: string } = {
   /*...*/
 };
Sign up to request clarification or add additional context in comments.

6 Comments

Many thanks! the in keyof was my missing piece. It works like a charm now.
@captain As far as I can see the ParamType is different for each request, so that type needs to be the one defining the keys.
@JonasWilms my point was mostly about using immutable objects instead of enum. I did not go deepr into question
@captain-yossarian - I saw that addition and added the as const to my real code. Very valuable! Thanks, I didn't know that this is possible
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.