0

I am currently struggling wrapping my head around a simple union type creation type.

For my use case I have a simple structure. I got a root object with strings as keys which holds object with strings for keys and some arbitrary value.

I now need the string type for the field access "path". For example {a:{b:22, c: false}} should result in the union 'a.b'|'a.c'.

I tried the following.

interface UnknownRecord {
  [key: string]: unknown;
}

type SecondIteration<Prefix extends string, Obj extends UnknownRecord> =
  keyof Obj extends string ?
    `${Prefix}.${keyof Obj}` :
    never;


interface DoubleUnknown {
  [name: string]: UnknownRecord;
}

type FirstIteration<Obj extends DoubleUnknown> =
  keyof Obj extends string ?
    Obj[keyof Obj] extends UnknownRecord ?
      SecondIteration<keyof Obj, Obj[keyof Obj]> :
      never :
    never;

const ONE_SIMPLE: { nr1a: number; nr2b: number; } = { nr1a: 1, nr2b: 1 };
const TWO_SIMPLE: { nr2a: number; nr2b: number; } = { nr2a: 2, nr2b: 2 };
const HELLO_SIMPLE: { str1: string } = { str1: 'hello' };
const FALSE_SIMPLE: { bln: boolean } = { bln: false };

const inputs = { ONE_SIMPLE, TWO_SIMPLE, HELLO_SIMPLE, FALSE_SIMPLE };

const firstExample: SecondIteration<'test', typeof ONE_SIMPLE> = 'test.nr2b';
const secondExample: FirstIteration<typeof inputs> = 'ONE_SIMPLE.nr1a';

The first example with just one level of depth works just fine, the second sadly does not. TypeScript argues that it should be never. Interestingly my IDE knows, for some reason, that the current string value is valid.

Suggestion by IDE

But after accepting one of the suggestions, it changes its mind:

enter image description here

The TypeScript playground agrees that never is the correct type, so I have no clue where the first suggestion comes from.

If you want to try the playground for yourself.

Do you have any suggestion where the problem might be or how to fix it? I would be very grateful for any input.

Feirell
  • 719
  • 9
  • 26
  • 1
    Will there always be only two levels of nesting? Then you could use [this](https://tsplay.dev/WPpGLW). – kelsny Nov 18 '22 at 18:37
  • 1
    Is the question primarily about how to get dotted paths to the leaf nodes of an object as mentioned [here](https://stackoverflow.com/q/58434389/2887218)? If so then you can do [this](https://tsplay.dev/w8xZpw). Does that meet your needs or am I missing something? (Pls ping @jcalz if you reply) – jcalz Nov 18 '22 at 19:00
  • 1
    @jcalz Hahah, yeah had my head banging on the keyboard :D Added the remaining post :) – Feirell Nov 18 '22 at 20:58
  • @caTS and jcalz Thank you very much guys for your help! It is really nice to have a working solution now! But I would really like to understand why my version did not work, so I am able to write those myself in future :) Do you got any ideas why that might be? – Feirell Nov 18 '22 at 21:00
  • 1
    Try `keyof ({ a: string } | { b: string })`. Is it what you expected? – kelsny Nov 18 '22 at 21:04
  • @caTS Not really, but I dont apply `keyof` on a object union type, or do I and I just don't see it? So not quite sure how it applies here. I just gone through the `FirsIteration` type and tried all section to see if I get never somewhere. – Feirell Nov 18 '22 at 21:35
  • `Obj[keyof Obj]` is probably a union. – jcalz Nov 19 '22 at 00:28
  • So, what is your primary question here, is it "why is my code not working" or "how do I accomplish my goal"? You're kind of asking both, but they are different (albeit related) questions. Whichever one is primary, you should probably [edit] the question to make it very clear which one would constitute an answer to the question as asked. Personally I prefer "how do I accomplish my goal" because it allows for a general-purpose answer suitable for future readers, whereas "why is my code not working" feels more like tech support for an individual. But it's up to you! – jcalz Nov 19 '22 at 00:28
  • @jcalz I do get your point about specificy but in almost all cases the person who asks a question intends on understanding the problem and solution. You did provide me with one, but I would have liked to understand why my solution did not work. Otherwise it does not really "help" since I will be back witha similar question in a couple of weeks. I do recon that my post, especially the last sentence, does clarify that I wish to undestand, why my approach did not yield the desired results. But I appriciate your solution! – Feirell Nov 19 '22 at 12:49

0 Answers0