Skip to content

Instantly share code, notes, and snippets.

@sladg
Created June 20, 2022 09:25
Show Gist options
  • Save sladg/c17c0c6628ad670b56d34095a061a270 to your computer and use it in GitHub Desktop.
Save sladg/c17c0c6628ad670b56d34095a061a270 to your computer and use it in GitHub Desktop.
Safely pick nested properties from object using dot-path.
type GetIndexedField<T, K> = K extends keyof T
? T[K]
: K extends `${number}`
? '0' extends keyof T
? undefined
: number extends keyof T
? T[number]
: undefined
: undefined;
type FieldWithPossiblyUndefined<T, Key> =
| GetFieldType<Exclude<T, undefined>, Key>
| Extract<T, undefined>;
type IndexedFieldWithPossiblyUndefined<T, Key> =
| GetIndexedField<Exclude<T, undefined>, Key>
| Extract<T, undefined>;
export type GetFieldType<T, P> = P extends `${infer Left}.${infer Right}`
? Left extends keyof T
? FieldWithPossiblyUndefined<T[Left], Right>
: Left extends `${infer FieldKey}[${infer IndexKey}]`
? FieldKey extends keyof T
? FieldWithPossiblyUndefined<IndexedFieldWithPossiblyUndefined<T[FieldKey], IndexKey>, Right>
: undefined
: undefined
: P extends keyof T
? T[P]
: P extends `${infer FieldKey}[${infer IndexKey}]`
? FieldKey extends keyof T
? IndexedFieldWithPossiblyUndefined<T[FieldKey], IndexKey>
: undefined
: undefined;
export function _get<TData, TPath extends string, TDefault = GetFieldType<TData, TPath>>(
data: TData,
path: TPath,
defaultValue?: TDefault,
): GetFieldType<TData, TPath> | TDefault {
const value = path
.split(/[.[\]]/)
.filter(Boolean)
.reduce<GetFieldType<TData, TPath>>((value, key) => (value as any)?.[key], data as any);
return value !== undefined ? value : (defaultValue as TDefault);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment