cefn / lauf

Monorepo for lauf packages.
MIT License
5 stars 0 forks source link

Adopt NestedKeyOf #180

Open cefn opened 2 years ago

cefn commented 2 years ago

Consider adopting NestedKeyOf from https://medium.com/xgeeks/typescript-utility-keyof-nested-object-fa3e457ef2b2

type NestedKeyOf<ObjectType extends object> = 
{[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object 
? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}`
: `${Key}`
}[keyof ObjectType & (string | number)];

It allows inference of paths like "something.deeper.still" meaning it would be easy to compose a Partitioned state via a valid key.

cefn commented 1 year ago

The example source below provides a type for the path of an object, and the type for a value given the object and path...

It is based on nestjs path logic... https://github.com/nestjs/config/blob/master/lib/types/path-value.type.ts

const tree = {
    grandparent:{
        parent:{
            child:{
                hands:{
                    left:{
                        fingers:[0,1,2,3,4]
                    },
                    right:{
                        fingers:[0,1,2,3,4]
                    }
                }
            }
        }
    }
} as const;

export type PathImpl<T, Key extends keyof T> = Key extends string
  ? T[Key] extends Record<string, any>
    ?
        | `${Key}.${PathImpl<T[Key], Exclude<keyof T[Key], keyof any[]>> &
            string}`
        | `${Key}.${Exclude<keyof T[Key], keyof any[]> & string}`
    : never
  : never;

export type DescendantPath<T> = PathImpl<T, keyof T> | keyof T;

export type Path<T> = keyof T extends string
  ? DescendantPath<T> extends string | keyof T
      ? DescendantPath<T>
      : keyof T
    :never

export type PathValue<
  T,
  P extends Path<T>,
> = P extends `${infer Key}.${infer Rest}`
  ? Key extends keyof T
    ? Rest extends Path<T[Key]>
      ? PathValue<T[Key], Rest>
      : never
    : never
  : P extends keyof T
  ? T[P]
  : never;

const treePath: Path<typeof tree> = "grandparent.parent.child.hands";
type FingersType = PathValue<typeof tree, typeof treePath>