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]
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']
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
Pre-edit answer (perhaps still relevant to cases where generics wouldn't apply):
Does not do the trick (as
nested
isany
). :(
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"
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