0

I have a function that takes a json key as a parameter. In order to allow autocompletion, I have typed this parameter. It works.

However, if I write the key with dynamic value, ex: CAR.BRAND.${color}, typescript recognises it as an invalid string, that doesn't match the required schema.

How to allow random strings as a parameter while keeping the autocompletion?

Here is the code:

// json
{
  hello: "hello",
  world: "world",
  name: {
    john: "john",
    doe: "doe"
  }
}

// .ts file
type KeyPrefix<T extends string> = T extends "" ? "" : `.${T}`;

export type KeyPath<T> = (
  T extends object
    ? {
        [K in Exclude<keyof T, symbol>]: `${K}${KeyPrefix<KeyPath<T[K]>>}`;
      }[Exclude<keyof T, symbol>]
    : ""
) extends infer D
  ? Extract<D, string>
  : never;

 type Path = KeyPath<typeof myjson>;

 function foo(key: Path){
    return key
 }
 
 // expected output:
 foo("nam") // autocomplete suggestions: name.john, name.doe. 
 foo("name.doe") // no error.
 foo("name." + value) // no error. (currently returns an error)
 

I've tried to allow a random string entry like this:

type i18nSchema<T> = T & { [key: string]: string };
type Path = KeyPath<i18nSchema<typeof myjson>>;

It removes the error, but the autocompletion is gone.

How to fix this?

DoneDeal0
  • 5,273
  • 13
  • 55
  • 114
  • 1
    A snippet only makes sense when its features (e.g. executing the code in it) adds something useful to the question. An unrelated syntax error doesn't help the reader of the question. – Andreas Aug 27 '21 at 08:20
  • I've replaced the foo function by a very basic one on purpose. The original foo() is very long, and doesn't add any value to the question, which is about typing a parameter. The function output doesn't matter here. – DoneDeal0 Aug 27 '21 at 08:23
  • Does this answer your question? [Eliminate nevers to make union possible](https://stackoverflow.com/questions/68668055/eliminate-nevers-to-make-union-possible) – captain-yossarian from Ukraine Aug 27 '21 at 08:48
  • You can also find this answer helpful https://stackoverflow.com/questions/67242871/declare-a-type-that-allows-all-parts-of-all-levels-of-another-type#answer-67247652 and my article https://catchts.com/deep-pick – captain-yossarian from Ukraine Aug 27 '21 at 08:48
  • Hi @captain-yossarian, I've tried your version, but it doesn't work. The autocomplete suggest incomplete paths (suggests `user` in addition to `user.john`, and `user.doe`), and returns an error if I write a dynamic string. Here is a codesandbox with the example: https://codesandbox.io/s/modest-robinson-8b1cj?file=/src/App.tsx:0-762 – DoneDeal0 Aug 27 '21 at 09:26
  • If you meant that it does not work because ` foo("name." + value)` than it is not completely true because TS considers this operation unsafe. Try this ` foo(`name.${value}`)` - it works – captain-yossarian from Ukraine Aug 27 '21 at 09:32
  • I get a `Argument of type 'string' is not assignable to parameter of type '"hello" | "world" | "name.john" | "name.doe"'.` – DoneDeal0 Aug 27 '21 at 09:47

0 Answers0