import has from 'lodash/has'; function hasKey( item: T, maybeKeyOfT: string | keyof T, ): maybeKeyOfT is keyof T { return has(item, maybeKeyOfT); } export type DataPropertyMap = Record; const getValueAt = (item: T, dataPropertyMap?: DataPropertyMap) => (maybeCurlyProperty: string) => { const maybeCurlySegment = /^{(.*)}$/.exec(maybeCurlyProperty)?.[1]; if (maybeCurlySegment) { const maybeKeyOfT = dataPropertyMap == null ? maybeCurlySegment : dataPropertyMap[maybeCurlySegment]?.find((prop) => hasKey(item, prop), ); if (maybeKeyOfT != null && hasKey(item, maybeKeyOfT)) { return String(item[maybeKeyOfT]); } } return maybeCurlyProperty; }; export function replaceUrlCurlySegments( templateUrl: URL, item: T, dataPropertyMap?: DataPropertyMap, ) { const url = new URL(templateUrl.href); url.pathname = templateUrl.pathname .split('/') .map(decodeURIComponent) .map(getValueAt(item, dataPropertyMap)) .map(encodeURIComponent) .join('/'); const getItemValueAt = getValueAt(item, dataPropertyMap); for (const [key, value] of Array.from(templateUrl.searchParams.entries())) { url.searchParams.set(key, getItemValueAt(value)); } return url; }