Home > Software engineering >  Keys of a nested "typed" object in typescript
Keys of a nested "typed" object in typescript

Time:02-18

Given the following construct:

type MyStruckt =  { hello: string; nested: any }

const MY_OBJ: MyStruct = {
  hello: "Hello",
  nested: {
    world: "World",
  },
};

How do I get the keys of nested?

type X = typeof MY_OBJ["nested"];
type NestedKeys = keyof typeof MY_OBJ["nested"];

Does not do the trick (as nested is any). :(

What I want to write is a hook-like function :

function useMyStruct(obj:MyStruct) {
   type Key = keyof typeof obj["nested"] 
   return function print(key:Key) {
      console.log(obj.nested[key])
   }
}

const printer = useMyStruct(MY_OBJ)
printer("world") // should be OK
printer("something") // should be an error

[Here is a playground][1]

https://www.typescriptlang.org/play?ssl=32&ssc=43&pln=22&pc=1#code/C4TwDgpgBAsiDKwBOBXAxsKBeKUDeUAFhADYkD2AXFAM7ICWAdgOYDcUjEdEAJtQIaMQUAL4AoMWnKM6sAJoB9APIAhAFLU4iVBmz4xuYmSpQARAAlSFUwBoDHLsF7U893AHdySEnzMB1Lx9bexE7EVYJMQB6KKhzcncoHnIoAEkoZghMYGIoAGsIEBoocgAzKAADTm4eCoB SNBIKAANPSaIMvlldQBtU2qnHlMAXQiOqAA5R14AaULinAKQLo6umEVVNX7B3lGIiRioABFyLg5yTGSoHOgGNDyoAAp YqqZ2qh6YsEQAEoAHRQShPaKxMFQPyEfiYdLuQTZFLuJD0JxfH5EcjkPIAWhI9AKUFKKEYGHo0mBkWJpOA5MYUBQNAgWmQ6GAT3IACMAFaUFk6YB-fS4G7gaDzYRLQqrMVdLncnYfUZQNxILIoJD06lkilgFGMdnLSgSoWuEUiqQycgkCAAijMDk8gG7Hi9ZYjP5ucTiSTSWR6phOJB6RnMhCsjBPDY9NSegMGiBIJ6mTzeYZCo40QjkFA KCc6BKWZieNB5M0cgAWyyhCYzFMGdiWZzeYLUEEUETSC8QA

CodePudding user response:

You can use a transitional object, optionally typed as const depending on your needs

type MyStruckt =  { hello: string; nested: any }

const obj0 = {
  hello: "Hello",
  nested: {
    world: "World",
  },
} as const;
const obj1: MyStruckt = obj0;
type X = typeof obj0['nested']

Playground

CodePudding user response:

Re your edit:

What I want to write is a hook-like function :

function useMyStruct(obj:MyStruct) {
   type Key = keyof typeof obj["nested"] 
   return function print(key:Key) {
      console.log(obj.nested[key])
   }
}

const printer = useMyStruct(MY_OBJ)
printer("world") // should be OK
printer("something") // should be an error

You can do that using a generic type parameter to your function:

function useMyStruct<ObjectType extends MyStruct>(obj: ObjectType) {
    type Key = keyof typeof obj["nested"];
    return function print(key: Key) {
        console.log(obj.nested[key]);
    };
}

const printer = useMyStruct(MY_OBJ);
printer("world");       // works
printer("something");   // error as desired

Playground link


Pre-edit answer (perhaps still relevant to cases where generics wouldn't apply):

Does not do the trick (as nested is any). :(

That's because the type of nested is any, because you've said MY_OBJ is a MyStruckt. So although the runtime value of nested is an object with a property called world, the compile-time type of nested is any, because that's what you've expressly told the compiler it is.

If you don't tell the compiler that MY_OBJ is of type MyStruckt, MY_OBJ is still assignment-compatible with MyStruckt but you can get the keys of the nested object:

type MyStruckt =  { hello: string; nested: any }

const MY_OBJ = {
  hello: "Hello",
  nested: {
    world: "World",
  },
};

type X = typeof MY_OBJ["nested"];
type NestedKeys = keyof typeof MY_OBJ["nested"]; // "world"

Playground link

That only works if MY_OBJ is declared literally in the code, as it is in your example. If it isn't (if you receive it as a MyStruckt parameter or similar), the only compile-time type information you can get for nested is any (from MyStruckt).

CodePudding user response:

Ok, this works:

type MyStruct =  { hello: string; nested: any }

// This function ensures that the obj is of type MyStruct,
// but does not alter the type of the obj
function makeStruct<T extends MyStruct>(obj:T) {
  return obj
}

const MY_OBJ = makeStruct({
  hello: "Hello",
  nested: {
    world: "World",
  },
})

function useMyStruct<T extends MyStruct>(obj:T) {
   type Key = keyof typeof obj["nested"] 
   return function print(key:Key) {
      console.log(obj.nested[key])
   }
}

const printer = useMyStruct(MY_OBJ)
printer("world") // should be OK
printer("something") // should be an error
  • Related