#Get nested object as string path

22 messages · Page 1 of 1 (latest)

wanton tusk
#

Not sure how to explain it, but I think the example explains it well.

type Path = {
  "/user": {
    "get": any
    "post": any
  },
  "/posts": {
    "get": any
    "post": any
  }
}

Should make a type that returns

type FullPath = "/user/get" | "/user/post" | "/post/get" | "/post/post"

Generally, I'm not really sure what keyof T actually returns, and therefor not sure how I can use it to manipulate types.

chilly jacinth
#

do you really have anys in there or is that just an example? i ask because any can be indexed with any property name, so really your paths would be like "user/get/${string}"

#

if you don't have anys, here's one approach:

lucid elbowBOT
#
mkantor#0

Preview:```ts
...
type PathsOf<T> = {
[K in string & keyof T]: T[K] extends Record<
string,
unknown

? `${K}/${PathsOf<T[K]>}`
: K

}[string & keyof T]
...```

wanton tusk
#

@chilly jacinthdude that's perfect, are object props stored like this? Record<string, unknown> if so I think I understand it

chilly jacinth
#

sort of. that's checking if the value is an object type in a roundabout way, by seeing if it can be indexed with strings to produce unknown values. it works because types have implicit index signatures like that

#

as much as i try to avoid the object type, in this case it may make things clearer:

lucid elbowBOT
#
mkantor#0

Preview:ts ... type PathsOf<T> = { [K in string & keyof T]: T[K] extends object ? `${K}/${PathsOf<T[K]>}` : K }[string & keyof T] ...

chilly jacinth
#

note that you didn't specify what should happen for types like arrays or functions, so i didn't go out of my way to handle them. results might not be what you expect, but you can add extra conditions if needed

wanton tusk
#

don't worry this got me on the right path (pun not intended)

#

I don't understand this part though [string & keyof T] at the end

chilly jacinth
#

that's using indexed access to get the properties of the object we created with the mapped type

#

here's a simpler example of the syntax:

lucid elbowBOT
#
type Foo = {
  a: string
  b: number
  c: boolean
}

type Bar = Foo['a']
//   ^? - type Bar = string
type Baz = Foo['a' | 'b']
//   ^? - type Baz = string | number
type Quux = Foo[keyof Foo]
//   ^? - type Quux = string | number | boolean
chilly jacinth
#

T[keyof T] ends up computing a union of all the property types

#

so in that mapped type we spit out an object with the paths as property values, but we just want a flat union of the paths

wanton tusk
#

okayyy, its's starting to make sense, what about the string &?

#

and thanks ALOT btw, this is a big help

chilly jacinth
#

sure thing

#

the string & is just a quick way to throw out non-string keys. in the key part of the mapping i do K in string & keyof T to ignore symbols, since symbols can't be interpolated anyway (type X = `${symbol}` is an error). then i need to use that same index at the end to pull out the property values i computed in the mapped type

wanton tusk
#

ooooh shoot dude I understand now

#

thank you!