0

I have a general understanding that type assertions are bad. Given that, I'm trying to write the following function without making use of one:

function stringifyQuery<T extends Record<string, unknown>>(
  query: T
): { [K in keyof T]: string } {
  return Object.fromEntries(
    Object.entries(query).map(([key, value]) => {
      return [key, JSON.stringify(value)]
    })
  )
}

but I get the type error: TS2322: Type '{ [k: string]: string; }' is not assignable to type '{ [K in keyof T]: string; }'.

Is there a way to convince the compiler this is correct without resorting to a type assertion?

I'm also open to changing the implementation if there's some more type-safe functions that can accomplish this.

leafmeal
  • 1,824
  • 15
  • 15
  • 1
    Some interesting reading in this question/answer: https://stackoverflow.com/questions/69019873/how-can-i-get-typed-object-entries-and-object-fromentries-in-typescript – Roar S. May 02 '23 at 00:43
  • 1
    The maxim "type assertions are bad" is either untrue or true-with-lots-of-caveats. Since `Object.fromEntries()` and `Object.entries()` aren't strongly typed enough for your purposes, you'll need to work around it. It would take even more type assertions or the like to try to enhance the typing of those methods (and you'd probably be lying about some of them; excess properties are tricky). As long as you're convinced the function is actually type safe (is it possible for the output to miss any of the required keys of `T`?), a single type assertion like you've got is warranted. – jcalz May 02 '23 at 03:03
  • ^.. does that fully address the question? If so I could maybe write up an answer explaining; if not, what am I missing? – jcalz May 02 '23 at 03:03
  • Then again... there's always [this](https://tsplay.dev/wOlZ6N) madness. – kelsny May 02 '23 at 14:57
  • @zenly I don't fully understand your comment. It looks like it typechecks? Could you write up an answer explaining it's strengths or potential drawbacks? – leafmeal May 02 '23 at 15:26
  • (Not who you asked, but I'm following this question since I'm waiting for an answer to my previous comment) That's a single-call-signature overload, and overload implementation signatures are checked loosely against their call signatures; it's pretty much the same as a type assertion in terms of safety. It can sometimes be more convenient than type assertions, but it's no safer. – jcalz May 02 '23 at 15:36
  • @jcalz thanks for explaining and sorry, I should have responded to your earlier comment. I think it might be worth putting it into an answer, your solution is very practical (and probably what I'll end up doing). I'm still hoping and holding out for a solution that accomplishes this without an assertion, if just to satisfy my intellectual curiosity. – leafmeal May 02 '23 at 16:51
  • I'm not particularly enthusiastic about putting in the effort to write up a full answer if you're likely to wait around indefinitely for a solution that's not forthcoming. There are certainly ways to avoid type assertions (e.g., use `any` or a function that produces `any`) but none where the compiler is guaranteeing any more safety ... and I'm not sure I want to stick around playing whack-a-mole with alternative solutions that might appear to be safer to you because they are better at hiding their unsoundness. Maybe I'll come back here later if anything changes. Good luck! – jcalz May 02 '23 at 16:58

0 Answers0