Home > Enterprise >  Strongly type a recursively nested proxy
Strongly type a recursively nested proxy

Time:03-08

I have some objects that I store content for some UI. They look like this:

const copy = {
  header: {
    content: 'Page Header'
  },
  main: {
    header: {
      content: 'content sub header'
    }
    body: {
      content: 'lorem ipsum....'
    }
  }
}

I have written a proxy which lets me get an ID, generated by the deeply nested path of the content I am accessing:

const isObject = (obj: unknown): obj is Record<string, any> =>
  typeof obj === 'object' && obj != null

const createHander = <T extends Record<string, any>>(path: string[]) => ({
  get: (target: T, key: string): any => {
    if (key === 'id') {
      return path.join('-')
    }
    const newTarget = target[key]
    if (isObject(newTarget)) {
      return new Proxy(newTarget, createHander([...path, key]))
    }
    return newTarget
  },
})

export const withIds = (copyObject: Record<string, any>, initialId: string) =>
  new Proxy(copyObject, createHander([initialId]))

so the following is true:

const copyWithIds = withIds(copy, 'page')
copyWithIds.main.body.id // page-main-body

Trouble is, everything on the copyWithId's object is now of type any

I would like to not be reliant on the any type as the return of the get but struggling to find a set up that works

CodePudding user response:

You can use a mapped type to emulate what you are doing in the function code


type WithId<T> = {
    [P in keyof T]: T[P] extends object ? WithId<T[P]> : never 
} & {
    id: string
}
export const withIds = <T extends Record<string, any>>(copyObject: T, initialId: string) =>
  new Proxy(copyObject, createHander([initialId])) as unknown as WithId<T>

Playground Link

  • Related