Add typing using keyof for nested keys at the same level

59 views Asked by At

Working on a game with a group, and it had a little encyclopedia. I was able to get some of it from JS to TS, which I'm slowly learning. But the structure has changed. Say the structure looks like the following:

const enycData = [
  {
  "Items": {
    "Apple": {...},
    ...
  },
  "Locations": {
    "Apple Land": {...},
    ...
  },
  ...
]

In practice, we will only ever be calling "Apple" or "Apple Land" in our functions based on the way everything else goes, and I'd like to be able to catch typos: openEncyc("Apple"). I expect each of these properties will be unique.

I don't need to do any arbitrary crawling, it will always be that deep. An array of objects representing categories, and then each article title in a category.

I had seen some guides using things like T extends T ? keyof T: never but I couldn't quite get it to click.

2

There are 2 answers

5
Unmitigated On

You could use a mapped type like so:

type K = {[i in Exclude<keyof typeof enycData, keyof []>]: 
           {[k in keyof typeof enycData[i]]: 
              keyof typeof enycData[i][k]}[keyof typeof enycData[i]]}[
              Exclude<keyof typeof enycData, keyof []>];
// "Apple" | "Apple Land"

This can be simplified with a helper type to get a union of the value types of an object.

type Values<T> = T[keyof T];
type K = Values<{[i in Exclude<keyof typeof enycData, keyof []>]: Values<{[k in keyof typeof enycData[i]]: keyof typeof enycData[i][k]}>}>;
0
Etienne Laurin On

The T extends T ? keyof T : never pattern can be used with the right T, which can, for example, be obtained with infer

type Keys = typeof encycData extends Record<string, infer T>[] ?
    T extends T ? keyof T : never
  : never;