remeda
Version:
A utility library for JavaScript and Typescript.
1 lines • 4.15 kB
Source Map (JSON)
{"version":3,"file":"setPath.cjs","names":["purry"],"sources":["../src/setPath.ts"],"sourcesContent":["import type { ValueOf } from \"type-fest\";\nimport type { RemedaTypeError } from \"./internal/types/RemedaTypeError\";\nimport { purry } from \"./purry\";\n\ntype Paths<T, Prefix extends ReadonlyArray<unknown> = []> =\n | Prefix\n | (T extends object\n ? ValueOf<{\n [K in ProperKeyOf<T>]-?: Paths<T[K], [...Prefix, K]>;\n }>\n : RemedaTypeError<\n \"setPath\",\n \"Can only compute paths objects\",\n { type: never; metadata: T }\n >) extends infer Path\n ? // The Paths type is used to define the path param in `setPath`. In order\n // for both mutable arrays and readonly arrays to be supported we need to\n // make all results `readonly` (because mutable arrays extend readonly\n // arrays, but not the other way around). Because the result of Paths is\n // a union of arrays we need to distribute Result so that the operator is\n // applied to each member separately.\n Readonly<Path>\n : never;\n\n/**\n * Array objects have all Array.prototype keys in their \"keyof\" type, which\n * is not what we'd expect from the operator. We only want the numeric keys\n * which represent proper elements of the array.\n */\ntype ProperKeyOf<T> = Extract<\n keyof T,\n T extends ReadonlyArray<unknown> ? number : keyof T\n>;\n\ntype ValueAtPath<T, Path> = Path extends readonly [\n infer Head extends keyof T,\n ...infer Rest,\n]\n ? ValueAtPath<T[Head], Rest>\n : T;\n\n/**\n * Sets the value at `path` of `object`.\n *\n * For simple cases where the path is only one level deep, prefer `set` instead.\n *\n * @param data - The target method.\n * @param path - The array of properties.\n * @param value - The value to set.\n * @signature\n * R.setPath(obj, path, value)\n * @example\n * R.setPath({ a: { b: 1 } }, ['a', 'b'], 2) // => { a: { b: 2 } }\n * @dataFirst\n * @category Object\n */\nexport function setPath<T, Path extends Paths<T>>(\n data: T,\n path: Path,\n value: ValueAtPath<T, Path>,\n): T;\n\n/**\n * Sets the value at `path` of `object`.\n *\n * @param path - The array of properties.\n * @param value - The value to set.\n * @signature\n * R.setPath(path, value)(obj)\n * @example\n * R.pipe({ a: { b: 1 } }, R.setPath(['a', 'b'], 2)) // { a: { b: 2 } }\n * @dataLast\n * @category Object\n */\nexport function setPath<\n T,\n Path extends Paths<T>,\n // TODO [>2] -- TODO: The following eslint is solvable by inlining Value and wrapping the T parameter with `NoInfer` (e.g. `ValueAtPath<NoInfer<T>, TPath>); to prevent typescript from inferring it as `unknown`. This is only available in TS 5.4, which is above what we currently support (5.1).\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- See TODO above...\n Value extends ValueAtPath<T, Path>,\n>(path: Path, value: Value): (data: T) => T;\n\nexport function setPath(...args: ReadonlyArray<unknown>): unknown {\n return purry(setPathImplementation, args);\n}\n\nfunction setPathImplementation(\n data: unknown,\n path: ReadonlyArray<PropertyKey>,\n value: unknown,\n): unknown {\n const [pivot, ...rest] = path;\n if (pivot === undefined) {\n return value;\n }\n\n if (Array.isArray(data)) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const copy = [...data];\n copy[pivot as number] = setPathImplementation(\n data[pivot as number],\n rest,\n value,\n );\n return copy;\n }\n\n const { [pivot]: currentValue, ...remaining } = data as Record<\n PropertyKey,\n unknown\n >;\n\n return {\n ...remaining,\n [pivot]: setPathImplementation(currentValue, rest, value),\n };\n}\n"],"mappings":"wCAkFA,SAAgB,EAAQ,GAAG,EAAuC,CAChE,OAAOA,EAAAA,EAAM,EAAuB,EAAK,CAG3C,SAAS,EACP,EACA,EACA,EACS,CACT,GAAM,CAAC,EAAO,GAAG,GAAQ,EACzB,GAAI,IAAU,IAAA,GACZ,OAAO,EAGT,GAAI,MAAM,QAAQ,EAAK,CAAE,CAEvB,IAAM,EAAO,CAAC,GAAG,EAAK,CAMtB,MALA,GAAK,GAAmB,EACtB,EAAK,GACL,EACA,EACD,CACM,EAGT,GAAM,EAAG,GAAQ,EAAc,GAAG,GAAc,EAKhD,MAAO,CACL,GAAG,GACF,GAAQ,EAAsB,EAAc,EAAM,EAAM,CAC1D"}