type GetIndexedField = K extends keyof T ? T[K] : K extends `${number}` ? 'length' extends keyof T ? number extends T['length'] ? number extends keyof T ? T[number] : undefined : undefined : undefined : undefined; type FieldWithPossiblyUndefined = | GetFieldType, Key> | Extract; type IndexedFieldWithPossiblyUndefined = | GetIndexedField, Key> | Extract; type GetFieldType = P extends `${infer Left}.${infer Right}` ? Left extends keyof Exclude ? | FieldWithPossiblyUndefined[Left], Right> | Extract : Left extends `${infer FieldKey}[${infer IndexKey}]` ? FieldKey extends keyof T ? FieldWithPossiblyUndefined< IndexedFieldWithPossiblyUndefined, Right > : undefined : undefined : P extends keyof T ? T[P] : P extends `${infer FieldKey}[${infer IndexKey}]` ? FieldKey extends keyof T ? IndexedFieldWithPossiblyUndefined : undefined : IndexedFieldWithPossiblyUndefined; type PropertyName = string | number | symbol; const rePropName = RegExp( // Match anything that isn't a dot or bracket. '[^.[\\]]+' + '|' + // Or match property names within brackets. '\\[(?:' + // Match a non-string expression. '([^"\'][^[]*)' + '|' + // Or match strings (supports escaping characters). '(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' + ')\\]' + '|' + // Or match "" as the space between consecutive dots or empty brackets. '(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))', 'g', ); /** * Casts `value` to a path array if it's not one. * * @private * @param {*} value The value to inspect. * @param {Object} [object] The object to query keys on. * @returns {Array} Returns the cast property path array. */ function castPath( path: TKey, obj: TObject, ): [TKey]; function castPath( path: TPath, obj: TObject, ): Exclude, null | undefined>; function castPath(value: string): string[] { const result: string[] = []; let match: RegExpExecArray | null; while ((match = rePropName.exec(value))) { result.push(match[3] ?? match[1]?.trim() ?? match[0]); } return result; } export function getProp( obj: TObject, path: TKey, defaultValue: TObject[TKey], ): TObject[TKey]; export function getProp( obj: TObject, path: TPath, ): Exclude, null | undefined>; export function getProp< TObject, TPath extends string, TDefault = GetFieldType, >( obj: TObject, path: TPath, defaultValue: TDefault, ): Exclude, null | undefined> | TDefault; export function getProp( obj: any, path: PropertyName, defaultValue?: T, ): T | undefined { if (path in obj) { const value = obj[path]; return value === undefined ? defaultValue : value; } const processedPath = Array.isArray(path) ? path : castPath(path, obj); let currentValue = obj; for (const key of processedPath) { currentValue = currentValue?.[key]; if (currentValue === undefined) return defaultValue; } return currentValue; } export function flattenReducer(acc: Array, arr: Array | T): Array { try { // This is faster but susceptible to `RangeError: Maximum call stack size exceeded` Array.isArray(arr) ? acc.push(...arr) : acc.push(arr); return acc; } catch (err: unknown) { // Fallback to a slower but safer option return acc.concat(arr); } } export function fastJoin(arr: Array, separator: string): string { let isFirst = true; return arr.reduce((acc, elem) => { if (elem === null || elem === undefined) { elem = '' as T; } if (isFirst) { isFirst = false; return `${elem}`; } return `${acc}${separator}${elem}`; }, ''); }