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.
But after accepting one of the suggestions, it changes its mind:
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.