Skip to content

Instantly share code, notes, and snippets.

@thehappycoder
Last active June 8, 2018 23:27
Show Gist options
  • Save thehappycoder/5e86ff798517d7599c8af6b1abf2c574 to your computer and use it in GitHub Desktop.
Save thehappycoder/5e86ff798517d7599c8af6b1abf2c574 to your computer and use it in GitHub Desktop.
Typesafe path function as an alternative to safe navigation operator
// Need to use this special type so that keyof would work
export type TheType<T> = Required<NonNullable<T>>
export function path<P extends keyof TheType<T>, T>(
props: [P],
obj: T
): PropType<P, T>
export function path<
P extends keyof TheType<T>,
P2 extends keyof TheType<T>[P],
T
>(props: [P, P2], obj: T): PropType2<P, P2, T>
export function path<
P extends keyof TheType<T>,
P2 extends keyof TheType<T>[P],
P3 extends keyof Required<TheType<T>[P]>[P2],
T
>(props: [P, P2, P3], obj: T): PropType3<P, P2, P3, T>
export function path<
P extends keyof TheType<T>,
P2 extends keyof TheType<T>[P],
P3 extends keyof Required<TheType<T>[P]>[P2],
P4 extends keyof Required<Required<TheType<T>[P]>[P2]>[P3],
T
>(props: [P, P2, P3, P4], obj: T): PropType4<P, P2, P3, P4, T>
export function path<
P extends keyof TheType<T>,
P2 extends keyof TheType<T>[P],
P3 extends keyof Required<TheType<T>[P]>[P2],
P4 extends keyof Required<Required<TheType<T>[P]>[P2]>[P3],
T
>(props: [P] | [P, P2] | [P, P2, P3] | [P, P2, P3, P4], obj: T): any {
if (obj === null || obj === undefined) {
return obj
}
const [prop, ...tail] = props
const value = (obj as any)[prop]
if (value === null || value === undefined) {
return value
}
if (props.length === 1) {
return value
}
return path(tail as any, value)
}
export type PropType<P extends keyof TheType<T>, T> = undefined extends T
? TheType<T>[P] | undefined
: TheType<T>[P]
export type PropType2<
P extends keyof TheType<T>,
P2 extends keyof TheType<T>[P],
T
> = TheType<T>[P] extends object
? undefined extends T
? PropType<P2, TheType<T>[P]> | undefined
: PropType<P2, TheType<T>[P]>
: never
export type PropType3<
P extends keyof TheType<T>,
P2 extends keyof TheType<T>[P],
P3 extends keyof Required<TheType<T>[P]>[P2],
T
> = TheType<T>[P] extends object
? undefined extends T
? PropType2<P2, P3, TheType<T>[P]> | undefined
: PropType2<P2, P3, TheType<T>[P]>
: never
export type PropType4<
P extends keyof TheType<T>,
P2 extends keyof TheType<T>[P],
P3 extends keyof Required<TheType<T>[P]>[P2],
P4 extends keyof Required<Required<TheType<T>[P]>[P2]>[P3],
T
> = TheType<T>[P] extends object
? undefined extends T
? PropType3<P2, P3, P4, TheType<T>[P]> | undefined
: PropType3<P2, P3, P4, TheType<T>[P]>
: never
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment