I'm looking for nested object paths: Is something like this possible?
interface IHuman {
age: number;
friend: {
name: string;
}
}
keyof IHuman; // "age", "friend.name"
I'm looking for nested object paths: Is something like this possible?
interface IHuman {
age: number;
friend: {
name: string;
}
}
keyof IHuman; // "age", "friend.name"
interface Obj {
a: {
x: string
y: {
z: string
}
}
b: {
u: boolean
}
c: number
}
type D = DotBranch<Obj> // type D = "c" | "a.x" | "a.y.z" | "b.u"
declare function get(path: DotBranch<Obj>)
get('') // Argument of type '""' is not assignable to parameter of type '"c" | "a.x" | "a.y.z" | "b.u"'.
type DotJoin<A extends string, B extends string> = A extends '' ? B : `${A}.${B}`
type DeepKeys<O extends AnyObject> = {
[K in keyof O]: O[K] extends AnyObject ? K : never
}[keyof O]
// @ts-expect-error Type 'keyof O' does not satisfy the constraint 'string'.
type DotBranch<O extends AnyObject, P extends string = '', K extends string = keyof O> =
K extends DeepKeys<O>
? DotBranch<O[K], DotJoin<P, K>>
: /*************/ DotJoin<P, K>
type AnyObject = Record<string, any>
See linked gist or playground v2 below to peek under the hood and copy the code.
type DotType<O extends AnyObject, T extends string & DotBranch<O>> = T extends `${infer A}.${infer B}`
// @ts-expect-error B of type string is not assignable to the constraint
? DotType<O[A], B>
: O[T]
const obj = {
a: {
x: 'xx',
y: {
z: 'zz',
},
},
b: {
u: true,
},
c: 42,
} as const
type V = DotType<typeof obj, 'c'> // type V = 42
declare function get<P extends DotBranch<Obj>>(path: P): DotType<Obj, P>
const leaf = getLeaf('a.y.z') // leaf: 'zz'
I don't think this is possible, as for why i can make a wild guess:
interface IHuman {
age: number;
'friend.name': any;
friend: {
name: string;
}
}
const b: keyof IHuman = 'friend.name';
friend.name
would now be ambigious.
I've not found a nice solution for this too. But you might want to take a look at typed-path project.
It's impossible because there are no string literal type operators in TypeScript. Given the string literals "foo"
and "bar"
, there is no programmatic way to get the string literal "foo.bar"
from the type systm. There are several feature suggestions filed in GitHub which might, if implemented, make this possible, like key augmentation or regular-expression string validation. But it doesn't look like they are being actively worked on.