@thi.ng/paths
Advanced tools
Comparing version 3.0.5 to 4.0.0
12
api.d.ts
@@ -1,11 +0,3 @@ | ||
import type { Keys, Keys1, Keys2, Keys3, Keys4, Keys5, Keys6, Keys7 } from "@thi.ng/api"; | ||
export declare type Path1<T, A extends Keys<T>> = [A]; | ||
export declare type Path2<T, A extends Keys<T>, B extends Keys1<T, A>> = [A, B]; | ||
export declare type Path3<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>> = [A, B, C]; | ||
export declare type Path4<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>> = [A, B, C, D]; | ||
export declare type Path5<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>> = [A, B, C, D, E]; | ||
export declare type Path6<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>> = [A, B, C, D, E, F]; | ||
export declare type Path7<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>> = [A, B, C, D, E, F, G]; | ||
export declare type Path8<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>> = [A, B, C, D, E, F, G, H]; | ||
export declare type UpdateFn<T> = (curr: T, ...args: any[]) => T; | ||
import type { FnO } from "@thi.ng/api"; | ||
export declare type UpdateFn<A, B> = FnO<A, B>; | ||
//# sourceMappingURL=api.d.ts.map |
@@ -6,34 +6,27 @@ # Change Log | ||
## [3.0.5](https://github.com/thi-ng/umbrella/compare/@thi.ng/paths@3.0.4...@thi.ng/paths@3.0.5) (2020-03-06) | ||
# [4.0.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/paths@3.0.5...@thi.ng/paths@4.0.0) (2020-03-28) | ||
**Note:** Version bump only for package @thi.ng/paths | ||
### Code Refactoring | ||
* **paths:** update path value inference ([ab4440e](https://github.com/thi-ng/umbrella/commit/ab4440e6a297559ceb824c5e4b3c7e023ae69710)) | ||
### Features | ||
## [3.0.4](https://github.com/thi-ng/umbrella/compare/@thi.ng/paths@3.0.3...@thi.ng/paths@3.0.4) (2020-02-26) | ||
* **paths:** add/update unsafe type infer, update doc strings ([5cc5b46](https://github.com/thi-ng/umbrella/commit/5cc5b461e9602011b62c49d8d4a6756e1ad4a404)) | ||
* **paths:** major API update ([b51efc6](https://github.com/thi-ng/umbrella/commit/b51efc69834e178344c4d1c1e47961460acedd8f)) | ||
* **paths:** update typed path sigs ([0b6c155](https://github.com/thi-ng/umbrella/commit/0b6c155d8d6cf9bd3f25bfce723cac2de48ad544)) | ||
**Note:** Version bump only for package @thi.ng/paths | ||
### BREAKING CHANGES | ||
* **paths:** update generics for `UpdateFn` | ||
- UpdateFn now takes input & output type generics | ||
## [3.0.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/paths@3.0.2...@thi.ng/paths@3.0.3) (2020-02-26) | ||
**Note:** Version bump only for package @thi.ng/paths | ||
## [3.0.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/paths@3.0.1...@thi.ng/paths@3.0.2) (2020-02-25) | ||
**Note:** Version bump only for package @thi.ng/paths | ||
# [3.0.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/paths@2.1.6...@thi.ng/paths@3.0.0) (2019-11-30) | ||
@@ -40,0 +33,0 @@ |
import type { Keys, Keys1, Keys2, Keys3, Keys4, Keys5, Keys6, Keys7, Path, Without, Without2, Without3, Without4, Without5, Without6, Without7, Without8 } from "@thi.ng/api"; | ||
/** | ||
* Uses {@link updateIn} and returns updated state with key for given | ||
* path removed. Does not modify original state.Returns `undefined` if | ||
* `path` is an empty string or array. | ||
* Unchecked version of {@link deleteIn}. Path can be given as string or | ||
* tuple. | ||
* | ||
* ``` | ||
* @example | ||
* ```ts | ||
* // unchecked | ||
* deleteIn({ a: { b: { c: 23 } } }, "a.b.c"); | ||
@@ -15,17 +16,34 @@ * // { a: { b: { } } } | ||
*/ | ||
export declare const deleteIn: (state: any, path: Path) => Pick<any, never>; | ||
export declare const deleteInUnsafe: (state: any, path: Path) => any; | ||
/** | ||
* Type checked version of {@link deleteIn}. | ||
* Uses {@link updateIn} and returns updated state with key for given | ||
* path removed. Does not modify original state. Returns `undefined` if | ||
* `path` is an empty string or array. | ||
* | ||
* @remarks | ||
* Only the first 8 path levels are type checked. The result type will | ||
* have the path value removed too. | ||
* | ||
* See {@link deleteInUnsafe} for unchecked version. | ||
* | ||
* @example | ||
* ```ts | ||
* // type checked | ||
* deleteIn({ a: { b: { c: 23 } } }, ["a","b","c"]); | ||
* | ||
* // error (invalid path) | ||
* deleteIn({ a: { b: { c: 23 } } }, ["a","b","d"]); | ||
* ``` | ||
* | ||
* @param state - | ||
* @param path - | ||
*/ | ||
export declare function deleteInT<T, A extends Keys<T>>(state: T, path: [A]): Without<T, A>; | ||
export declare function deleteInT<T, A extends Keys<T>, B extends Keys1<T, A>>(state: T, path: [A, B]): Without2<T, A, B>; | ||
export declare function deleteInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>>(state: T, path: [A, B, C]): Without3<T, A, B, C>; | ||
export declare function deleteInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>>(state: T, path: [A, B, C, D]): Without4<T, A, B, C, D>; | ||
export declare function deleteInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>>(state: T, path: [A, B, C, D, E]): Without5<T, A, B, C, D, E>; | ||
export declare function deleteInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>>(state: T, path: [A, B, C, D, E, F]): Without6<T, A, B, C, D, E, F>; | ||
export declare function deleteInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>>(state: T, path: [A, B, C, D, E, F, G]): Without7<T, A, B, C, D, E, F, G>; | ||
export declare function deleteInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(state: T, path: [A, B, C, D, E, F, G, H]): Without8<T, A, B, C, D, E, F, G, H>; | ||
export declare function deleteIn<T, A extends Keys<T>>(state: T, path: readonly [A]): Without<T, A>; | ||
export declare function deleteIn<T, A extends Keys<T>, B extends Keys1<T, A>>(state: T, path: readonly [A, B]): Without2<T, A, B>; | ||
export declare function deleteIn<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>>(state: T, path: readonly [A, B, C]): Without3<T, A, B, C>; | ||
export declare function deleteIn<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>>(state: T, path: readonly [A, B, C, D]): Without4<T, A, B, C, D>; | ||
export declare function deleteIn<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>>(state: T, path: readonly [A, B, C, D, E]): Without5<T, A, B, C, D, E>; | ||
export declare function deleteIn<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>>(state: T, path: readonly [A, B, C, D, E, F]): Without6<T, A, B, C, D, E, F>; | ||
export declare function deleteIn<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>>(state: T, path: readonly [A, B, C, D, E, F, G]): Without7<T, A, B, C, D, E, F, G>; | ||
export declare function deleteIn<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(state: T, path: readonly [A, B, C, D, E, F, G, H]): Without8<T, A, B, C, D, E, F, G, H>; | ||
//# sourceMappingURL=delete-in.d.ts.map |
import { toPath } from "./path"; | ||
import { updateInT } from "./update-in"; | ||
import { updateIn } from "./update-in"; | ||
/** | ||
* Uses {@link updateIn} and returns updated state with key for given | ||
* path removed. Does not modify original state.Returns `undefined` if | ||
* `path` is an empty string or array. | ||
* Unchecked version of {@link deleteIn}. Path can be given as string or | ||
* tuple. | ||
* | ||
* ``` | ||
* @example | ||
* ```ts | ||
* // unchecked | ||
* deleteIn({ a: { b: { c: 23 } } }, "a.b.c"); | ||
@@ -16,9 +17,9 @@ * // { a: { b: { } } } | ||
*/ | ||
export const deleteIn = (state, path) => deleteInT(state, path); | ||
export function deleteInT(state, path) { | ||
const ks = [...toPath(path)]; | ||
if (ks.length > 0) { | ||
export const deleteInUnsafe = (state, path) => deleteIn(state, path); | ||
export function deleteIn(state, path) { | ||
const ks = toPath(path).slice(); | ||
if (ks.length) { | ||
const k = ks.pop(); | ||
return updateInT(state, ks, (x) => ((x = Object.assign({}, x)), delete x[k], x)); | ||
return updateIn(state, ks, (x) => ((x = Object.assign({}, x)), delete x[k], x)); | ||
} | ||
} |
@@ -1,18 +0,14 @@ | ||
import type { Keys, Keys1, Keys2, Keys3, Keys4, Keys5, Keys6, Keys7, Path, Val1, Val2, Val3, Val4, Val5, Val6, Val7, Val8 } from "@thi.ng/api"; | ||
import type { DeepPath, Path, Path0, Path1, Path3, Path4, Path5, Path6, Path7, Path8, OptPathVal } from "@thi.ng/api"; | ||
/** | ||
* Immediate use getter, i.e. same as: `getter(path)(state)`. | ||
* Unchecked version of {@link getIn}. Returns `undefined` if path is | ||
* invalid. | ||
* | ||
* @remarks | ||
* Supports type checked paths and values for path lengths <= 8. String | ||
* paths are always unchecked (i.e. `state` is `any`). | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be retrieved (default: `any`). | ||
* | ||
* @example | ||
* ```ts | ||
* // checked path and inferred return type | ||
* getIn({ a: { b: { c: 23 } } }, ["a","b","c"]); | ||
* getInUnsafe({ a: { b: { c: 23 } } }, "a.b.c"); | ||
* // 23 | ||
* | ||
* // unchecked path | ||
* getIn({ a: { b: { c: 23 } } }, "a.b.c"); | ||
* // 23 | ||
* ``` | ||
@@ -23,19 +19,30 @@ * | ||
*/ | ||
export declare const getIn: (state: any, path: Path) => any; | ||
export declare const getInUnsafe: <T = any>(state: any, path: Path) => T | undefined; | ||
/** | ||
* Type checked version of {@link getIn}. | ||
* Type checked, immediate use getter, i.e. same as: | ||
* `defGetter(path)(state)`. | ||
* | ||
* @remarks | ||
* Only the first 8 path levels are type checked. | ||
* | ||
* @example | ||
* ```ts | ||
* // type checked path and inferred return type | ||
* getIn({ a: { b: { c: 23 } } }, ["a","b","c"]); | ||
* // 23 | ||
* ``` | ||
* | ||
* @param state - | ||
* @param path - | ||
*/ | ||
export declare function getInT<T>(state: T, path: []): T; | ||
export declare function getInT<T, A extends Keys<T>>(state: T, path: [A]): Val1<T, A>; | ||
export declare function getInT<T, A extends Keys<T>, B extends Keys1<T, A>>(state: T, path: [A, B]): Val2<T, A, B>; | ||
export declare function getInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>>(state: T, path: [A, B, C]): Val3<T, A, B, C>; | ||
export declare function getInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>>(state: T, path: [A, B, C, D]): Val4<T, A, B, C, D>; | ||
export declare function getInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>>(state: T, path: [A, B, C, D, E]): Val5<T, A, B, C, D, E>; | ||
export declare function getInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>>(state: T, path: [A, B, C, D, E, F]): Val6<T, A, B, C, D, E, F>; | ||
export declare function getInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>>(state: T, path: [A, B, C, D, E, F, G]): Val7<T, A, B, C, D, E, F, G>; | ||
export declare function getInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(state: T, path: [A, B, C, D, E, F, G, H]): Val8<T, A, B, C, D, E, F, G, H>; | ||
export declare function getInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(state: T, path: [A, B, C, D, E, F, G, H, ...any[]]): any; | ||
export declare function getIn<T>(state: T, path: Path0): T; | ||
export declare function getIn<T, A>(state: T, path: Path1<T, A>): OptPathVal<T, [A]>; | ||
export declare function getIn<T, A, B>(state: T, path: Path1<A, B>): OptPathVal<T, [A, B]>; | ||
export declare function getIn<T, A, B, C>(state: T, path: Path3<T, A, B, C>): OptPathVal<T, [A, B, C]>; | ||
export declare function getIn<T, A, B, C, D>(state: T, path: Path4<T, A, B, C, D>): OptPathVal<T, [A, B, C, D]>; | ||
export declare function getIn<T, A, B, C, D, E>(state: T, path: Path5<T, A, B, C, D, E>): OptPathVal<T, [A, B, C, D, E]>; | ||
export declare function getIn<T, A, B, C, D, E, F>(state: T, path: Path6<T, A, B, C, D, E, F>): OptPathVal<T, [A, B, C, D, E, F]>; | ||
export declare function getIn<T, A, B, C, D, E, F, G>(state: T, path: Path7<T, A, B, C, D, E, F, G>): OptPathVal<T, [A, B, C, D, E, F, G]>; | ||
export declare function getIn<T, A, B, C, D, E, F, G, H>(state: T, path: Path8<T, A, B, C, D, E, F, G, H>): OptPathVal<T, [A, B, C, D, E, F, G, H]>; | ||
export declare function getIn<T, A, B, C, D, E, F, G, H>(state: T, path: DeepPath<T, A, B, C, D, E, F, G, H>): any; | ||
//# sourceMappingURL=get-in.d.ts.map |
@@ -1,18 +0,14 @@ | ||
import { getterT } from "./getter"; | ||
import { defGetter } from "./getter"; | ||
/** | ||
* Immediate use getter, i.e. same as: `getter(path)(state)`. | ||
* Unchecked version of {@link getIn}. Returns `undefined` if path is | ||
* invalid. | ||
* | ||
* @remarks | ||
* Supports type checked paths and values for path lengths <= 8. String | ||
* paths are always unchecked (i.e. `state` is `any`). | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be retrieved (default: `any`). | ||
* | ||
* @example | ||
* ```ts | ||
* // checked path and inferred return type | ||
* getIn({ a: { b: { c: 23 } } }, ["a","b","c"]); | ||
* getInUnsafe({ a: { b: { c: 23 } } }, "a.b.c"); | ||
* // 23 | ||
* | ||
* // unchecked path | ||
* getIn({ a: { b: { c: 23 } } }, "a.b.c"); | ||
* // 23 | ||
* ``` | ||
@@ -23,5 +19,5 @@ * | ||
*/ | ||
export const getIn = (state, path) => getterT(path)(state); | ||
export function getInT(state, path) { | ||
return getterT(path)(state); | ||
export const getInUnsafe = (state, path) => defGetter(path)(state); | ||
export function getIn(state, path) { | ||
return defGetter(path)(state); | ||
} |
@@ -1,22 +0,42 @@ | ||
import type { Fn, Keys, Keys1, Keys2, Keys3, Keys4, Keys5, Keys6, Keys7, Path, Val1, Val2, Val3, Val4, Val5, Val6, Val7, Val8 } from "@thi.ng/api"; | ||
import type { DeepPath, Fn, Path, Path0, Path1, Path2, Path3, Path4, Path5, Path6, Path7, Path8, OptPathVal } from "@thi.ng/api"; | ||
/** | ||
* Composes a getter function for given nested lookup path. Optimized | ||
* fast execution paths are provided for path lengths <= 4. | ||
* Unchecked version of {@link defGetter}. | ||
* | ||
* @remarks | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be retrieved (default: `any`). | ||
* | ||
* Also see: {@link getIn}, {@link getInUnsafe} | ||
* | ||
* @example | ||
* ```ts | ||
* const g = defGetterUnsafe("a.b.c"); | ||
* | ||
* g({ a: { b: { c: 23} } }) // 23 | ||
* g({ x: 23 }) // undefined | ||
* g() // undefined | ||
* ``` | ||
* | ||
* @param path - | ||
*/ | ||
export declare const defGetterUnsafe: <T = any>(path: Path) => Fn<any, T | undefined>; | ||
/** | ||
* Creates getter function for given nested lookup path. Returns | ||
* function which accepts single object and returns value at given path. | ||
* | ||
* @remarks | ||
* Optimized fast execution paths are provided for path lengths <= 4. | ||
* Only the first 8 path levels are type checked. | ||
* | ||
* Supports any `[]`-indexable data structure (arrays, objects, | ||
* strings). | ||
* | ||
* If `path` is given as string, it will be split using `.`. Returns | ||
* function which accepts single object and when called, returns value | ||
* at given path. | ||
* If any intermediate key is not present in the given obj, further | ||
* descent stops and the function returns `undefined`. | ||
* | ||
* If any intermediate key is not present in the given obj, descent | ||
* stops and the function returns `undefined`. | ||
* If `path` is an empty array, the returned getter will simply return | ||
* the given state arg (aka identity function). | ||
* | ||
* If `path` is an empty string or array, the returned getter will | ||
* simply return the given state arg (identity function). | ||
* Also see: {@link defGetterUnsafe}, {@link getIn}, {@link getInUnsafe} | ||
* | ||
* Also see: `getIn()` | ||
* | ||
* @example | ||
@@ -28,14 +48,11 @@ * ```ts | ||
* | ||
* // fully typed getter | ||
* g = getter<Foo, "a", "b", "c">(["a","b","c"]); | ||
* // fully type checked getter | ||
* g = defGetter<Foo, "a", "b", "c">(["a","b","c"]); | ||
* | ||
* // error (wrong `d` key) | ||
* g = getter<Foo, "a", "b", "d">(["a","b","d"]); | ||
* g = defGetter<Foo, "a", "b", "d">(["a","b","d"]); | ||
* | ||
* // unchecked (accepts any, returns any) | ||
* g = getter("a.b.c"); | ||
* | ||
* g({ a: { b: { c: 23} } }) // 23 | ||
* g({ x: 23 }) // undefined | ||
* g() // undefined | ||
* g({ a: { b: { c: 23} } }); // 23 | ||
* g({ x: 23 }); // error | ||
* g(); // error | ||
* ``` | ||
@@ -45,18 +62,12 @@ * | ||
*/ | ||
export declare const getter: (path: Path) => Fn<any, any>; | ||
/** | ||
* Type checked version of {@link getter}. | ||
* | ||
* @param path - | ||
*/ | ||
export declare function getterT<T>(path: []): Fn<T, T>; | ||
export declare function getterT<T, A extends Keys<T>>(path: [A]): Fn<T, Val1<T, A>>; | ||
export declare function getterT<T, A extends Keys<T>, B extends Keys1<T, A>>(path: [A, B]): Fn<T, Val2<T, A, B>>; | ||
export declare function getterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>>(path: [A, B, C]): Fn<T, Val3<T, A, B, C>>; | ||
export declare function getterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>>(path: [A, B, C, D]): Fn<T, Val4<T, A, B, C, D>>; | ||
export declare function getterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>>(path: [A, B, C, D, E]): Fn<T, Val5<T, A, B, C, D, E>>; | ||
export declare function getterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>>(path: [A, B, C, D, E, F]): Fn<T, Val6<T, A, B, C, D, E, F>>; | ||
export declare function getterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>>(path: [A, B, C, D, E, F, G]): Fn<T, Val7<T, A, B, C, D, E, F, G>>; | ||
export declare function getterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(path: [A, B, C, D, E, F, G, H]): Fn<T, Val8<T, A, B, C, D, E, F, G, H>>; | ||
export declare function getterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(path: [A, B, C, D, E, F, G, H, ...any[]]): Fn<T, any>; | ||
export declare function defGetter<T>(path: Path0): Fn<T, T>; | ||
export declare function defGetter<T, A>(path: Path1<T, A>): Fn<T, OptPathVal<T, [A]>>; | ||
export declare function defGetter<T, A, B>(path: Path2<T, A, B>): Fn<T, OptPathVal<T, [A, B]>>; | ||
export declare function defGetter<T, A, B, C>(path: Path3<T, A, B, C>): Fn<T, OptPathVal<T, [A, B, C]>>; | ||
export declare function defGetter<T, A, B, C, D>(path: Path4<T, A, B, C, D>): Fn<T, OptPathVal<T, [A, B, C, D]>>; | ||
export declare function defGetter<T, A, B, C, D, E>(path: Path5<T, A, B, C, D, E>): Fn<T, OptPathVal<T, [A, B, C, D, E]>>; | ||
export declare function defGetter<T, A, B, C, D, E, F>(path: Path6<T, A, B, C, D, E, F>): Fn<T, OptPathVal<T, [A, B, C, D, E, F]>>; | ||
export declare function defGetter<T, A, B, C, D, E, F, G>(path: Path7<T, A, B, C, D, E, F, G>): Fn<T, OptPathVal<T, [A, B, C, D, E, F, G]>>; | ||
export declare function defGetter<T, A, B, C, D, E, F, G, H>(path: Path8<T, A, B, C, D, E, F, G, H>): Fn<T, OptPathVal<T, [A, B, C, D, E, F, G, H]>>; | ||
export declare function defGetter<T, A, B, C, D, E, F, G, H>(path: DeepPath<T, A, B, C, D, E, F, G, H>): Fn<T, any>; | ||
//# sourceMappingURL=getter.d.ts.map |
import { toPath } from "./path"; | ||
/** | ||
* Composes a getter function for given nested lookup path. Optimized | ||
* fast execution paths are provided for path lengths <= 4. | ||
* Unchecked version of {@link defGetter}. | ||
* | ||
* @remarks | ||
* Supports any `[]`-indexable data structure (arrays, objects, | ||
* strings). | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be retrieved (default: `any`). | ||
* | ||
* If `path` is given as string, it will be split using `.`. Returns | ||
* function which accepts single object and when called, returns value | ||
* at given path. | ||
* Also see: {@link getIn}, {@link getInUnsafe} | ||
* | ||
* If any intermediate key is not present in the given obj, descent | ||
* stops and the function returns `undefined`. | ||
* | ||
* If `path` is an empty string or array, the returned getter will | ||
* simply return the given state arg (identity function). | ||
* | ||
* Also see: `getIn()` | ||
* | ||
* @example | ||
* ```ts | ||
* interface Foo { | ||
* a: { b: { c: number; } } | ||
* } | ||
* const g = defGetterUnsafe("a.b.c"); | ||
* | ||
* // fully typed getter | ||
* g = getter<Foo, "a", "b", "c">(["a","b","c"]); | ||
* | ||
* // error (wrong `d` key) | ||
* g = getter<Foo, "a", "b", "d">(["a","b","d"]); | ||
* | ||
* // unchecked (accepts any, returns any) | ||
* g = getter("a.b.c"); | ||
* | ||
* g({ a: { b: { c: 23} } }) // 23 | ||
@@ -44,6 +22,6 @@ * g({ x: 23 }) // undefined | ||
*/ | ||
export const getter = (path) => getterT(path); | ||
export function getterT(path) { | ||
export const defGetterUnsafe = (path) => defGetter(path); | ||
export function defGetter(path) { | ||
const ks = toPath(path); | ||
let [a, b, c, d] = ks; | ||
const [a, b, c, d] = ks; | ||
switch (ks.length) { | ||
@@ -50,0 +28,0 @@ case 0: |
127
lib/index.js
@@ -35,6 +35,6 @@ 'use strict'; | ||
const getter = (path) => getterT(path); | ||
function getterT(path) { | ||
const defGetterUnsafe = (path) => defGetter(path); | ||
function defGetter(path) { | ||
const ks = toPath(path); | ||
let [a, b, c, d] = ks; | ||
const [a, b, c, d] = ks; | ||
switch (ks.length) { | ||
@@ -77,6 +77,6 @@ case 0: | ||
const setter = (path) => setterT(path); | ||
function setterT(path) { | ||
const defSetterUnsafe = (path) => defSetter(path); | ||
function defSetter(path) { | ||
const ks = toPath(path); | ||
let [a, b, c, d] = ks; | ||
const [a, b, c, d] = ks; | ||
switch (ks.length) { | ||
@@ -86,8 +86,8 @@ case 0: | ||
case 1: | ||
return (s, v) => ((s = _copy(s)), (s[a] = v), s); | ||
return (s, v) => ((s = copy(s)), (s[a] = v), s); | ||
case 2: | ||
return (s, v) => { | ||
let x; | ||
s = _copy(s); | ||
s[a] = x = _copy(s[a]); | ||
s = copy(s); | ||
s[a] = x = copy(s[a]); | ||
x[b] = v; | ||
@@ -99,5 +99,5 @@ return s; | ||
let x, y; | ||
s = _copy(s); | ||
s[a] = x = _copy(s[a]); | ||
x[b] = y = _copy(x[b]); | ||
s = copy(s); | ||
s[a] = x = copy(s[a]); | ||
x[b] = y = copy(x[b]); | ||
y[c] = v; | ||
@@ -109,6 +109,6 @@ return s; | ||
let x, y, z; | ||
s = _copy(s); | ||
s[a] = x = _copy(s[a]); | ||
x[b] = y = _copy(x[b]); | ||
y[c] = z = _copy(y[c]); | ||
s = copy(s); | ||
s[a] = x = copy(s[a]); | ||
x[b] = y = copy(x[b]); | ||
y[c] = z = copy(y[c]); | ||
z[d] = v; | ||
@@ -125,26 +125,28 @@ return s; | ||
} | ||
const _copy = (s) => (checks.isArray(s) ? s.slice() : Object.assign({}, s)); | ||
const compS = (k, f) => (s, v) => ((s = _copy(s)), (s[k] = f ? f(s[k], v) : v), s); | ||
const copy = (x) => checks.isArray(x) || checks.isTypedArray(x) ? x.slice() : Object.assign({}, x); | ||
const compS = (k, f) => (s, v) => ((s = copy(s)), (s[k] = f ? f(s[k], v) : v), s); | ||
const updateIn = (state, path, fn, ...args) => updateInT(state, path, fn, ...args); | ||
function updateInT(state, path, fn, ...args) { | ||
return setterT(path)(state, fn.apply(null, (args.unshift(getterT(path)(state)), args))); | ||
const updateInUnsafe = (state, path, fn, ...args) => | ||
updateIn(state, path, fn, ...args); | ||
function updateIn(state, path, fn, ...args) { | ||
return defSetter(path)(state, | ||
fn.apply(null, (args.unshift(defGetter(path)(state)), args))); | ||
} | ||
const deleteIn = (state, path) => deleteInT(state, path); | ||
function deleteInT(state, path) { | ||
const ks = [...toPath(path)]; | ||
if (ks.length > 0) { | ||
const deleteInUnsafe = (state, path) => deleteIn(state, path); | ||
function deleteIn(state, path) { | ||
const ks = toPath(path).slice(); | ||
if (ks.length) { | ||
const k = ks.pop(); | ||
return updateInT(state, ks, (x) => ((x = Object.assign({}, x)), delete x[k], x)); | ||
return updateIn(state, ks, (x) => ((x = Object.assign({}, x)), delete x[k], x)); | ||
} | ||
} | ||
const getIn = (state, path) => getterT(path)(state); | ||
function getInT(state, path) { | ||
return getterT(path)(state); | ||
const getInUnsafe = (state, path) => defGetter(path)(state); | ||
function getIn(state, path) { | ||
return defGetter(path)(state); | ||
} | ||
const mutator = (path) => mutatorT(path); | ||
function mutatorT(path) { | ||
const defMutatorUnsafe = (path) => defMutator(path); | ||
function defMutator(path) { | ||
const ks = toPath(path); | ||
@@ -204,22 +206,22 @@ let [a, b, c, d] = ks; | ||
const mutIn = (state, path, val) => mutInT(state, path, val); | ||
function mutInT(state, path, val) { | ||
return mutatorT(path)(state, val); | ||
const mutInUnsafe = (state, path, val) => defMutator(path)(state, val); | ||
function mutIn(state, path, val) { | ||
return defMutator(path)(state, val); | ||
} | ||
const mutInMany = (state, ...pairs) => { | ||
function mutInManyUnsafe(state, ...pairs) { | ||
const n = pairs.length; | ||
n & 1 && errors.illegalArgs(`require even number of args (got ${pairs.length})`); | ||
for (let i = 0; i < n && state; i += 2) { | ||
state = mutInT(state, pairs[i], pairs[i + 1]); | ||
state = mutIn(state, pairs[i], pairs[i + 1]); | ||
} | ||
return state; | ||
}; | ||
} | ||
const setIn = (state, path, val) => setterT(path)(state, val); | ||
function setInT(state, path, val) { | ||
return setterT(path)(state, val); | ||
const setInUnsafe = (state, path, val) => defSetter(path)(state, val); | ||
function setIn(state, path, val) { | ||
return defSetter(path)(state, val); | ||
} | ||
const setInMany = (state, ...pairs) => { | ||
function setInManyUnsafe(state, ...pairs) { | ||
const n = pairs.length; | ||
@@ -229,35 +231,36 @@ n & 1 && | ||
for (let i = 0; i < n; i += 2) { | ||
state = setInT(state, pairs[i], pairs[i + 1]); | ||
state = setIn(state, pairs[i], pairs[i + 1]); | ||
} | ||
return state; | ||
}; | ||
} | ||
const updater = (path, fn) => updaterT(path, fn); | ||
function updaterT(path, fn) { | ||
const g = getterT(path); | ||
const s = setterT(path); | ||
const defUpdaterUnsafe = (path, fn) => defUpdater(path, fn); | ||
function defUpdater(path, fn) { | ||
const g = defGetter(path); | ||
const s = defSetter(path); | ||
return (state, ...args) => s(state, fn.apply(null, (args.unshift(g(state)), args))); | ||
} | ||
exports.copy = copy; | ||
exports.defGetter = defGetter; | ||
exports.defGetterUnsafe = defGetterUnsafe; | ||
exports.defMutator = defMutator; | ||
exports.defMutatorUnsafe = defMutatorUnsafe; | ||
exports.defSetter = defSetter; | ||
exports.defSetterUnsafe = defSetterUnsafe; | ||
exports.defUpdater = defUpdater; | ||
exports.defUpdaterUnsafe = defUpdaterUnsafe; | ||
exports.deleteIn = deleteIn; | ||
exports.deleteInT = deleteInT; | ||
exports.deleteInUnsafe = deleteInUnsafe; | ||
exports.exists = exists; | ||
exports.getIn = getIn; | ||
exports.getInT = getInT; | ||
exports.getter = getter; | ||
exports.getterT = getterT; | ||
exports.getInUnsafe = getInUnsafe; | ||
exports.mutIn = mutIn; | ||
exports.mutInMany = mutInMany; | ||
exports.mutInT = mutInT; | ||
exports.mutator = mutator; | ||
exports.mutatorT = mutatorT; | ||
exports.mutInManyUnsafe = mutInManyUnsafe; | ||
exports.mutInUnsafe = mutInUnsafe; | ||
exports.setIn = setIn; | ||
exports.setInMany = setInMany; | ||
exports.setInT = setInT; | ||
exports.setter = setter; | ||
exports.setterT = setterT; | ||
exports.setInManyUnsafe = setInManyUnsafe; | ||
exports.setInUnsafe = setInUnsafe; | ||
exports.toPath = toPath; | ||
exports.updateIn = updateIn; | ||
exports.updateInT = updateInT; | ||
exports.updater = updater; | ||
exports.updaterT = updaterT; | ||
exports.updateInUnsafe = updateInUnsafe; |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@thi.ng/checks"),require("@thi.ng/errors")):"function"==typeof define&&define.amd?define(["exports","@thi.ng/checks","@thi.ng/errors"],t):t(((e=e||self).thi=e.thi||{},e.thi.ng=e.thi.ng||{},e.thi.ng.paths={}),e.thi.ng.checks,e.thi.ng.errors)}(this,(function(e,t,n){"use strict";const r=e=>t.isArray(e)?e:t.isString(e)?e.length>0?e.split("."):[]:null!=e?[e]:[];function u(e){const t=r(e);let[n,u,l,s]=t;switch(t.length){case 0:return e=>e;case 1:return e=>null!=e?e[n]:void 0;case 2:return e=>null!=e&&null!=(e=e[n])?e[u]:void 0;case 3:return e=>null!=e&&null!=(e=e[n])&&null!=(e=e[u])?e[l]:void 0;case 4:return e=>null!=e&&null!=(e=e[n])&&null!=(e=e[u])&&null!=(e=e[l])?e[s]:void 0;default:return e=>{const n=t.length-1;let r=e;for(let e=0;null!=r&&e<=n;e++)r=r[t[e]];return r}}}function l(e){const t=r(e);let[n,u,l,o]=t;switch(t.length){case 0:return(e,t)=>t;case 1:return(e,t)=>((e=s(e))[n]=t,e);case 2:return(e,t)=>{let r;return(e=s(e))[n]=r=s(e[n]),r[u]=t,e};case 3:return(e,t)=>{let r,i;return(e=s(e))[n]=r=s(e[n]),r[u]=i=s(r[u]),i[l]=t,e};case 4:return(e,t)=>{let r,i,c;return(e=s(e))[n]=r=s(e[n]),r[u]=i=s(r[u]),i[l]=c=s(i[l]),c[o]=t,e};default:let e;for(let n=t.length;--n>=0;)e=i(t[n],e);return e}}const s=e=>t.isArray(e)?e.slice():Object.assign({},e),i=(e,t)=>(n,r)=>((n=s(n))[e]=t?t(n[e],r):r,n);function o(e,t,n,...r){return l(t)(e,n.apply(null,(r.unshift(u(t)(e)),r)))}function c(e,t){const n=[...r(t)];if(n.length>0){const t=n.pop();return o(e,n,e=>(delete(e=Object.assign({},e))[t],e))}}function a(e){const t=r(e);let[n,u,l,s]=t;switch(t.length){case 0:return(e,t)=>t;case 1:return(e,t)=>e?(e[n]=t,e):void 0;case 2:return(e,t)=>{let r;return e&&(r=e[n])?(r[u]=t,e):void 0};case 3:return(e,t)=>{let r;return e&&(r=e[n])&&(r=r[u])?(r[l]=t,e):void 0};case 4:return(e,t)=>{let r;return e&&(r=e[n])&&(r=r[u])&&(r=r[l])?(r[s]=t,e):void 0};default:return(e,n)=>{let r=e;const u=t.length-1;for(let e=0;e<u;e++)if(!(r=r[t[e]]))return;return r[t[u]]=n,e}}}function f(e,t,n){return a(t)(e,n)}function g(e,t,n){return l(t)(e,n)}function h(e,t){const n=u(e),r=l(e);return(e,...u)=>r(e,t.apply(null,(u.unshift(n(e)),u)))}e.deleteIn=(e,t)=>c(e,t),e.deleteInT=c,e.exists=(e,t)=>{if(null==e)return!1;for(let n=(t=r(t)).length-1,u=0;u<=n;u++){const r=t[u];if(!e.hasOwnProperty(r))return!1;if(null==(e=e[r])&&u<n)return!1}return!0},e.getIn=(e,t)=>u(t)(e),e.getInT=function(e,t){return u(t)(e)},e.getter=e=>u(e),e.getterT=u,e.mutIn=(e,t,n)=>f(e,t,n),e.mutInMany=(e,...t)=>{const r=t.length;1&r&&n.illegalArgs(`require even number of args (got ${t.length})`);for(let n=0;n<r&&e;n+=2)e=f(e,t[n],t[n+1]);return e},e.mutInT=f,e.mutator=e=>a(e),e.mutatorT=a,e.setIn=(e,t,n)=>l(t)(e,n),e.setInMany=(e,...t)=>{const r=t.length;1&r&&n.illegalArgs(`require even number of KV args (got ${t.length})`);for(let n=0;n<r;n+=2)e=g(e,t[n],t[n+1]);return e},e.setInT=g,e.setter=e=>l(e),e.setterT=l,e.toPath=r,e.updateIn=(e,t,n,...r)=>o(e,t,n,...r),e.updateInT=o,e.updater=(e,t)=>h(e,t),e.updaterT=h,Object.defineProperty(e,"__esModule",{value:!0})})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@thi.ng/checks"),require("@thi.ng/errors")):"function"==typeof define&&define.amd?define(["exports","@thi.ng/checks","@thi.ng/errors"],t):t(((e=e||self).thi=e.thi||{},e.thi.ng=e.thi.ng||{},e.thi.ng.paths={}),e.thi.ng.checks,e.thi.ng.errors)}(this,(function(e,t,n){"use strict";const r=e=>t.isArray(e)?e:t.isString(e)?e.length>0?e.split("."):[]:null!=e?[e]:[];function u(e){const t=r(e),[n,u,l,s]=t;switch(t.length){case 0:return e=>e;case 1:return e=>null!=e?e[n]:void 0;case 2:return e=>null!=e&&null!=(e=e[n])?e[u]:void 0;case 3:return e=>null!=e&&null!=(e=e[n])&&null!=(e=e[u])?e[l]:void 0;case 4:return e=>null!=e&&null!=(e=e[n])&&null!=(e=e[u])&&null!=(e=e[l])?e[s]:void 0;default:return e=>{const n=t.length-1;let r=e;for(let e=0;null!=r&&e<=n;e++)r=r[t[e]];return r}}}function l(e){const t=r(e),[n,u,l,o]=t;switch(t.length){case 0:return(e,t)=>t;case 1:return(e,t)=>((e=s(e))[n]=t,e);case 2:return(e,t)=>{let r;return(e=s(e))[n]=r=s(e[n]),r[u]=t,e};case 3:return(e,t)=>{let r,i;return(e=s(e))[n]=r=s(e[n]),r[u]=i=s(r[u]),i[l]=t,e};case 4:return(e,t)=>{let r,i,f;return(e=s(e))[n]=r=s(e[n]),r[u]=i=s(r[u]),i[l]=f=s(i[l]),f[o]=t,e};default:let e;for(let n=t.length;--n>=0;)e=i(t[n],e);return e}}const s=e=>t.isArray(e)||t.isTypedArray(e)?e.slice():Object.assign({},e),i=(e,t)=>(n,r)=>((n=s(n))[e]=t?t(n[e],r):r,n);function o(e,t,n,...r){return l(t)(e,n.apply(null,(r.unshift(u(t)(e)),r)))}function f(e,t){const n=r(t).slice();if(n.length){const t=n.pop();return o(e,n,e=>(delete(e=Object.assign({},e))[t],e))}}function c(e){const t=r(e);let[n,u,l,s]=t;switch(t.length){case 0:return(e,t)=>t;case 1:return(e,t)=>e?(e[n]=t,e):void 0;case 2:return(e,t)=>{let r;return e&&(r=e[n])?(r[u]=t,e):void 0};case 3:return(e,t)=>{let r;return e&&(r=e[n])&&(r=r[u])?(r[l]=t,e):void 0};case 4:return(e,t)=>{let r;return e&&(r=e[n])&&(r=r[u])&&(r=r[l])?(r[s]=t,e):void 0};default:return(e,n)=>{let r=e;const u=t.length-1;for(let e=0;e<u;e++)if(!(r=r[t[e]]))return;return r[t[u]]=n,e}}}function a(e,t,n){return c(t)(e,n)}function d(e,t,n){return l(t)(e,n)}function h(e,t){const n=u(e),r=l(e);return(e,...u)=>r(e,t.apply(null,(u.unshift(n(e)),u)))}e.copy=s,e.defGetter=u,e.defGetterUnsafe=e=>u(e),e.defMutator=c,e.defMutatorUnsafe=e=>c(e),e.defSetter=l,e.defSetterUnsafe=e=>l(e),e.defUpdater=h,e.defUpdaterUnsafe=(e,t)=>h(e,t),e.deleteIn=f,e.deleteInUnsafe=(e,t)=>f(e,t),e.exists=(e,t)=>{if(null==e)return!1;for(let n=(t=r(t)).length-1,u=0;u<=n;u++){const r=t[u];if(!e.hasOwnProperty(r))return!1;if(null==(e=e[r])&&u<n)return!1}return!0},e.getIn=function(e,t){return u(t)(e)},e.getInUnsafe=(e,t)=>u(t)(e),e.mutIn=a,e.mutInManyUnsafe=function(e,...t){const r=t.length;1&r&&n.illegalArgs(`require even number of args (got ${t.length})`);for(let n=0;n<r&&e;n+=2)e=a(e,t[n],t[n+1]);return e},e.mutInUnsafe=(e,t,n)=>c(t)(e,n),e.setIn=d,e.setInManyUnsafe=function(e,...t){const r=t.length;1&r&&n.illegalArgs(`require even number of KV args (got ${t.length})`);for(let n=0;n<r;n+=2)e=d(e,t[n],t[n+1]);return e},e.setInUnsafe=(e,t,n)=>l(t)(e,n),e.toPath=r,e.updateIn=o,e.updateInUnsafe=(e,t,n,...r)=>o(e,t,n,...r),Object.defineProperty(e,"__esModule",{value:!0})})); |
@@ -0,15 +1,21 @@ | ||
import type { Path } from "@thi.ng/api"; | ||
/** | ||
* Similar to {@link mutIn}, but takes any number of path-value pairs as | ||
* args and applies them in sequence using `mutIn()`. All key paths must | ||
* already be present in the given data structure until their | ||
* penultimate key. | ||
* Similar to {@link mutInUnsafe}, but takes any number of path-value | ||
* pairs as args and applies them in sequence using `mutInUnsafe()`. | ||
* | ||
* @remarks | ||
* Unlike {@link mutIn}, this function does not use type checked paths. | ||
* All intermediate path keys must already be present in the given data | ||
* structure until their penultimate key. Missing leaf keys are | ||
* supported. | ||
* | ||
* The type parameter `T` can be used to indicate the type of the state | ||
* value and will also be used as return type. | ||
* | ||
* @example | ||
* ```ts | ||
* mutInMany( | ||
* mutInManyUnsafe( | ||
* { a: { b: 1 }, x: { y: { z: 2 } } }, | ||
* // pair #1 | ||
* "a.b", 10, | ||
* // pair #2 | ||
* "x.y.z", 20 | ||
@@ -23,3 +29,7 @@ * ) | ||
*/ | ||
export declare const mutInMany: (state: any, ...pairs: any[]) => any; | ||
export declare function mutInManyUnsafe<T>(state: T, p1: Path, v1: any): T; | ||
export declare function mutInManyUnsafe<T>(state: T, p1: Path, v1: any, p2: Path, v2: any): T; | ||
export declare function mutInManyUnsafe<T>(state: T, p1: Path, v1: any, p2: Path, v2: any, p3: Path, v3: any): T; | ||
export declare function mutInManyUnsafe<T>(state: T, p1: Path, v1: any, p2: Path, v2: any, p3: Path, v3: any, p4: Path, v4: any): T; | ||
export declare function mutInManyUnsafe<T>(state: T, p1: Path, v1: any, p2: Path, v2: any, p3: Path, v3: any, p4: Path, v4: any, ...xs: any[]): T; | ||
//# sourceMappingURL=mut-in-many.d.ts.map |
import { illegalArgs } from "@thi.ng/errors"; | ||
import { mutInT } from "./mut-in"; | ||
/** | ||
* Similar to {@link mutIn}, but takes any number of path-value pairs as | ||
* args and applies them in sequence using `mutIn()`. All key paths must | ||
* already be present in the given data structure until their | ||
* penultimate key. | ||
* | ||
* @remarks | ||
* Unlike {@link mutIn}, this function does not use type checked paths. | ||
* | ||
* @example | ||
* ```ts | ||
* mutInMany( | ||
* { a: { b: 1 }, x: { y: { z: 2 } } }, | ||
* "a.b", 10, | ||
* "x.y.z", 20 | ||
* ) | ||
* // { a: { b: 10 }, x: { y: { z: 20 } } } | ||
* ``` | ||
* | ||
* @param state - | ||
* @param pairs - | ||
*/ | ||
export const mutInMany = (state, ...pairs) => { | ||
import { mutIn } from "./mut-in"; | ||
export function mutInManyUnsafe(state, ...pairs) { | ||
const n = pairs.length; | ||
n & 1 && illegalArgs(`require even number of args (got ${pairs.length})`); | ||
for (let i = 0; i < n && state; i += 2) { | ||
state = mutInT(state, pairs[i], pairs[i + 1]); | ||
state = mutIn(state, pairs[i], pairs[i + 1]); | ||
} | ||
return state; | ||
}; | ||
} |
@@ -1,19 +0,11 @@ | ||
import type { Keys, Keys1, Keys2, Keys3, Keys4, Keys5, Keys6, Keys7, Path, Val1, Val2, Val3, Val4, Val5, Val6, Val7, Val8 } from "@thi.ng/api"; | ||
import type { DeepPath, Path, Path0, Path1, Path2, Path3, Path4, Path5, Path6, Path7, Path8, PathVal } from "@thi.ng/api"; | ||
/** | ||
* Immediate use mutator, i.e. same as: `mutator(path)(state, val)`. | ||
* Unchecked version of {@link mutIn}. | ||
* | ||
* @remarks | ||
* Also see {@link setIn}, {@link updateIn}, {@link deleteIn}. | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be mutated (default: `any`). | ||
* | ||
* @example | ||
* ```ts | ||
* interface Foo { | ||
* a: { b: number[]; } | ||
* } | ||
* | ||
* // fully type checked | ||
* mutIn({ a: { b: [10, 20] } }, ["a", "b", 1], 23) | ||
* // { a: { b: [ 10, 23 ] } } | ||
* | ||
* // unchecked | ||
* mutIn({ a: { b: [10, 20] } }, "a.b.1", 23); | ||
@@ -31,6 +23,18 @@ * // { a: { b: [ 10, 23 ] } } | ||
*/ | ||
export declare const mutIn: (state: any, path: Path, val: any) => any; | ||
export declare const mutInUnsafe: <T = any>(state: any, path: Path, val: T) => any; | ||
/** | ||
* Type checked version of {@link mutIn}. | ||
* Type checked, immediate use mutator, i.e. same as: | ||
* `defMutator(path)(state, val)`. | ||
* | ||
* @remarks | ||
* Only the first 8 path levels are type checked. | ||
* | ||
* Also see {@link defMutator}, {@link mutInUnsafe} | ||
* | ||
* @example | ||
* ```ts | ||
* mutIn({ a: { b: [10, 20] } }, ["a", "b", 1], 23) | ||
* // { a: { b: [ 10, 23 ] } } | ||
* ``` | ||
* | ||
* @param state - | ||
@@ -40,12 +44,12 @@ * @param path - | ||
*/ | ||
export declare function mutInT<T>(state: T, path: [], val: T): T; | ||
export declare function mutInT<T, A extends Keys<T>>(state: T, path: [A], val: Val1<T, A>): T; | ||
export declare function mutInT<T, A extends Keys<T>, B extends Keys1<T, A>>(state: T, path: [A, B], val: Val2<T, A, B>): T; | ||
export declare function mutInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>>(state: T, path: [A, B, C], val: Val3<T, A, B, C>): T; | ||
export declare function mutInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>>(state: T, path: [A, B, C, D], val: Val4<T, A, B, C, D>): T; | ||
export declare function mutInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>>(state: T, path: [A, B, C, D, E], val: Val5<T, A, B, C, D, E>): T; | ||
export declare function mutInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>>(state: T, path: [A, B, C, D, E, F], val: Val6<T, A, B, C, D, E, F>): T; | ||
export declare function mutInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, V>(state: T, path: [A, B, C, D, E, F, G], val: Val7<T, A, B, C, D, E, F, G>): T; | ||
export declare function mutInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(state: T, path: [A, B, C, D, E, F, G, H], val: Val8<T, A, B, C, D, E, F, G, H>): T; | ||
export declare function mutInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(state: T, path: [A, B, C, D, E, F, G, H, ...PropertyKey[]], val: any): any; | ||
export declare function mutIn<T>(state: T, path: Path0, val: T): T; | ||
export declare function mutIn<T, A>(state: T, path: Path1<T, A>, val: PathVal<T, [A]>): T; | ||
export declare function mutIn<T, A, B>(state: T, path: Path2<T, A, B>, val: PathVal<T, [A, B]>): T; | ||
export declare function mutIn<T, A, B, C>(state: T, path: Path3<T, A, B, C>, val: PathVal<T, [A, B, C]>): T; | ||
export declare function mutIn<T, A, B, C, D>(state: T, path: Path4<T, A, B, C, D>, val: PathVal<T, [A, B, C, D]>): T; | ||
export declare function mutIn<T, A, B, C, D, E>(state: T, path: Path5<T, A, B, C, D, E>, val: PathVal<T, [A, B, C, D, E]>): T; | ||
export declare function mutIn<T, A, B, C, D, E, F>(state: T, path: Path6<T, A, B, C, D, E, F>, val: PathVal<T, [A, B, C, D, E, F]>): T; | ||
export declare function mutIn<T, A, B, C, D, E, F, G>(state: T, path: Path7<T, A, B, C, D, E, F, G>, val: PathVal<T, [A, B, C, D, E, F, G]>): T; | ||
export declare function mutIn<T, A, B, C, D, E, F, G, H>(state: T, path: Path8<T, A, B, C, D, E, F, G, H>, val: PathVal<T, [A, B, C, D, E, F, G, H]>): T; | ||
export declare function mutIn<T, A, B, C, D, E, F, G, H>(state: T, path: DeepPath<T, A, B, C, D, E, F, G, H>, val: any): any; | ||
//# sourceMappingURL=mut-in.d.ts.map |
@@ -1,19 +0,11 @@ | ||
import { mutatorT } from "./mutator"; | ||
import { defMutator } from "./mutator"; | ||
/** | ||
* Immediate use mutator, i.e. same as: `mutator(path)(state, val)`. | ||
* Unchecked version of {@link mutIn}. | ||
* | ||
* @remarks | ||
* Also see {@link setIn}, {@link updateIn}, {@link deleteIn}. | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be mutated (default: `any`). | ||
* | ||
* @example | ||
* ```ts | ||
* interface Foo { | ||
* a: { b: number[]; } | ||
* } | ||
* | ||
* // fully type checked | ||
* mutIn({ a: { b: [10, 20] } }, ["a", "b", 1], 23) | ||
* // { a: { b: [ 10, 23 ] } } | ||
* | ||
* // unchecked | ||
* mutIn({ a: { b: [10, 20] } }, "a.b.1", 23); | ||
@@ -31,5 +23,5 @@ * // { a: { b: [ 10, 23 ] } } | ||
*/ | ||
export const mutIn = (state, path, val) => mutInT(state, path, val); | ||
export function mutInT(state, path, val) { | ||
return mutatorT(path)(state, val); | ||
export const mutInUnsafe = (state, path, val) => defMutator(path)(state, val); | ||
export function mutIn(state, path, val) { | ||
return defMutator(path)(state, val); | ||
} |
@@ -1,7 +0,18 @@ | ||
import type { Fn2, Keys, Keys1, Keys2, Keys3, Keys4, Keys5, Keys6, Keys7, Path, Val1, Val2, Val3, Val4, Val5, Val6, Val7, Val8 } from "@thi.ng/api"; | ||
import type { DeepPath, Fn2, Path, Path0, Path1, Path2, Path3, Path4, Path5, Path6, Path7, Path8, PathVal } from "@thi.ng/api"; | ||
/** | ||
* Higher-order function, similar to {@link setter}. Returns function | ||
* which when called mutates given object/array at given path location. | ||
* Unchecked version of {@link defMutator}. | ||
* | ||
* @remarks | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be mutated (default: `any`). | ||
* | ||
* @param path - | ||
*/ | ||
export declare const defMutatorUnsafe: <T = any>(path: Path) => Fn2<any, T, any>; | ||
/** | ||
* Higher-order function, similar to {@link defSetter}. Returns a | ||
* function, which when called, mutates given object/array at given path | ||
* location. | ||
* | ||
* @remarks | ||
* The returned function bails if any intermediate path values are | ||
@@ -11,23 +22,20 @@ * non-indexable (only the very last path element can be missing in the | ||
* object, else `undefined`. This function provides optimized versions | ||
* for path lengths <= 4. Type checking is supported for path lengths <= | ||
* 8. | ||
* for path lengths <= 4. | ||
* | ||
* @param path - | ||
*/ | ||
export declare const mutator: (path: Path) => Fn2<any, any, any>; | ||
/** | ||
* Type checked version of {@link mutator}. | ||
* Only the first 8 path levels are type checked. | ||
* | ||
* Also see {@link defMutatorUnsafe}, {@link mutIn} | ||
* | ||
* @param path - | ||
*/ | ||
export declare function mutatorT<T>(path: []): Fn2<T, T, T>; | ||
export declare function mutatorT<T, A extends Keys<T>>(path: [A]): Fn2<T, Val1<T, A>, T>; | ||
export declare function mutatorT<T, A extends Keys<T>, B extends Keys1<T, A>>(path: [A, B]): Fn2<T, Val2<T, A, B>, T>; | ||
export declare function mutatorT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>>(path: [A, B, C]): Fn2<T, Val3<T, A, B, C>, T>; | ||
export declare function mutatorT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>>(path: [A, B, C, D]): Fn2<T, Val4<T, A, B, C, D>, T>; | ||
export declare function mutatorT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>>(path: [A, B, C, D, E]): Fn2<T, Val5<T, A, B, C, D, E>, T>; | ||
export declare function mutatorT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>>(path: [A, B, C, D, E, F]): Fn2<T, Val6<T, A, B, C, D, E, F>, T>; | ||
export declare function mutatorT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>>(path: [A, B, C, D, E, F, G]): Fn2<T, Val7<T, A, B, C, D, E, F, G>, T>; | ||
export declare function mutatorT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(path: [A, B, C, D, E, F, G, H]): Fn2<T, Val8<T, A, B, C, D, E, F, G, H>, T>; | ||
export declare function mutatorT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(path: [A, B, C, D, E, F, G, H, ...PropertyKey[]]): Fn2<T, any, any>; | ||
export declare function defMutator<T>(path: Path0): Fn2<T, T, T>; | ||
export declare function defMutator<T, A>(path: Path1<T, A>): Fn2<T, PathVal<T, [A]>, T>; | ||
export declare function defMutator<T, A, B>(path: Path2<T, A, B>): Fn2<T, PathVal<T, [A, B]>, T>; | ||
export declare function defMutator<T, A, B, C>(path: Path3<T, A, B, C>): Fn2<T, PathVal<T, [A, B, C]>, T>; | ||
export declare function defMutator<T, A, B, C, D>(path: Path4<T, A, B, C, D>): Fn2<T, PathVal<T, [A, B, C, D]>, T>; | ||
export declare function defMutator<T, A, B, C, D, E>(path: Path5<T, A, B, C, D, E>): Fn2<T, PathVal<T, [A, B, C, D, E]>, T>; | ||
export declare function defMutator<T, A, B, C, D, E, F>(path: Path6<T, A, B, C, D, E, F>): Fn2<T, PathVal<T, [A, B, C, D, E, F]>, T>; | ||
export declare function defMutator<T, A, B, C, D, E, F, G>(path: Path7<T, A, B, C, D, E, F, G>): Fn2<T, PathVal<T, [A, B, C, D, E, F, G]>, T>; | ||
export declare function defMutator<T, A, B, C, D, E, F, G, H>(path: Path8<T, A, B, C, D, E, F, G, H>): Fn2<T, PathVal<T, [A, B, C, D, E, F, G, H]>, T>; | ||
export declare function defMutator<T, A, B, C, D, E, F, G, H>(path: DeepPath<T, A, B, C, D, E, F, G, H>): Fn2<T, any, any>; | ||
//# sourceMappingURL=mutator.d.ts.map |
import { toPath } from "./path"; | ||
/** | ||
* Higher-order function, similar to {@link setter}. Returns function | ||
* which when called mutates given object/array at given path location. | ||
* Unchecked version of {@link defMutator}. | ||
* | ||
* @remarks | ||
* The returned function bails if any intermediate path values are | ||
* non-indexable (only the very last path element can be missing in the | ||
* actual object structure). If successful, returns original (mutated) | ||
* object, else `undefined`. This function provides optimized versions | ||
* for path lengths <= 4. Type checking is supported for path lengths <= | ||
* 8. | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be mutated (default: `any`). | ||
* | ||
* @param path - | ||
*/ | ||
export const mutator = (path) => mutatorT(path); | ||
export function mutatorT(path) { | ||
export const defMutatorUnsafe = (path) => defMutator(path); | ||
export function defMutator(path) { | ||
const ks = toPath(path); | ||
@@ -19,0 +14,0 @@ let [a, b, c, d] = ks; |
{ | ||
"name": "@thi.ng/paths", | ||
"version": "3.0.5", | ||
"version": "4.0.0", | ||
"description": "Immutable, optimized and optionally typed path-based object property / array accessors with structural sharing", | ||
@@ -24,3 +24,3 @@ "module": "./index.js", | ||
"clean": "rimraf *.js *.d.ts .nyc_output build coverage doc lib", | ||
"doc:readme": "../../scripts/generate-readme", | ||
"doc:readme": "ts-node -P ../../tools/tsconfig.json ../../tools/src/readme.ts", | ||
"doc": "node_modules/.bin/typedoc --mode modules --out doc src", | ||
@@ -42,4 +42,5 @@ "doc:ae": "mkdir -p .ae/doc .ae/temp && node_modules/.bin/api-extractor run --local --verbose", | ||
"dependencies": { | ||
"@thi.ng/checks": "^2.5.4", | ||
"@thi.ng/errors": "^1.2.7" | ||
"@thi.ng/checks": "^2.6.0", | ||
"@thi.ng/errors": "^1.2.8", | ||
"tslib": "^1.11.1" | ||
}, | ||
@@ -69,3 +70,3 @@ "keywords": [ | ||
"sideEffects": false, | ||
"gitHead": "18014ee1e4978dac7eb2e5d51d0a6ff7d82e9ffc" | ||
"gitHead": "202477e312cf26869f0421e42a9a5fd80ff6adc8" | ||
} |
@@ -18,3 +18,3 @@ import type { Path } from "@thi.ng/api"; | ||
*/ | ||
export declare const toPath: (path: Path) => (string | number | symbol)[]; | ||
export declare const toPath: (path: Path) => (string | number)[]; | ||
/** | ||
@@ -21,0 +21,0 @@ * Takes an arbitrary object and lookup path. Descends into object along |
288
README.md
<!-- This file is generated - DO NOT EDIT! --> | ||
# ![@thi.ng/paths](https://media.thi.ng/umbrella/banners/thing-paths.svg?1583078711) | ||
# ![@thi.ng/paths](https://media.thi.ng/umbrella/banners/thing-paths.svg?1585427333) | ||
@@ -14,2 +14,6 @@ [![npm version](https://img.shields.io/npm/v/@thi.ng/paths.svg)](https://www.npmjs.com/package/@thi.ng/paths) | ||
- [Status](#status) | ||
- [Breaking changes](#breaking-changes) | ||
- [4.0.0](#400) | ||
- [Naming convention](#naming-convention) | ||
- [Type checked accessors](#type-checked-accessors) | ||
- [Installation](#installation) | ||
@@ -19,4 +23,7 @@ - [Dependencies](#dependencies) | ||
- [API](#api) | ||
- [Accessors](#accessors) | ||
- [Type checked versions](#type-checked-versions) | ||
- [Type checked paths](#type-checked-paths) | ||
- [Optional property handling](#optional-property-handling) | ||
- [Higher-order accessors](#higher-order-accessors) | ||
- [First order operators](#first-order-operators) | ||
- [Deletions](#deletions) | ||
- [Structural sharing](#structural-sharing) | ||
@@ -36,2 +43,25 @@ - [Mutable setter](#mutable-setter) | ||
## Breaking changes | ||
### 4.0.0 | ||
#### Naming convention | ||
As part of a larger effort to enforce more consistent naming conventions | ||
across various umbrella packages, all higher-order operators in this | ||
package are now using the `def` prefix: e.g. `getterT()` => | ||
`defGetter()`, `setterT()` => `defSetter()`. | ||
#### Type checked accessors | ||
**Type checked accessors are now the default and those functions expect | ||
paths provided as tuples**. To continue using string based paths (e.g. | ||
`"a.b.c"`), alternative `Unsafe` versions are provided. E.g. `getIn()` | ||
(type checked) vs. `getInUnsafe()` (unchecked). Higher-order versions | ||
also provide fallbacks (e.g. `getter()` => `defGetterUnsafe()`). | ||
Type checking for paths is currently "only" supported for the first 8 | ||
levels of nesting. Deeper paths are supported but only partially checked | ||
and their value type inferred as `any`. | ||
## Installation | ||
@@ -43,3 +73,3 @@ | ||
Package sizes (gzipped): ESM: 1.0KB / CJS: 1.1KB / UMD: 1.1KB | ||
Package sizes (gzipped): ESM: 1.09 KB / CJS: 1.19 KB / UMD: 1.14 KB | ||
@@ -50,2 +80,3 @@ ## Dependencies | ||
- [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/develop/packages/errors) | ||
- [tslib](https://github.com/thi-ng/umbrella/tree/develop/packages/undefined) | ||
@@ -60,67 +91,107 @@ ## Usage examples | ||
### hdom-elm <!-- NOTOC --> | ||
| Screenshot | Description | Live demo | Source | | ||
| ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------- | | ||
| | Using hdom in an Elm-like manner | [Demo](https://demo.thi.ng/umbrella/hdom-elm/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/hdom-elm) | | ||
| | Event handling w/ interceptors and side effects | [Demo](https://demo.thi.ng/umbrella/interceptor-basics2/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/interceptor-basics2) | | ||
| | Basic SPA example with atom-based UI router | [Demo](https://demo.thi.ng/umbrella/login-form/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/login-form) | | ||
| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/rstream-event-loop.png" width="240"/> | Minimal demo of using rstream constructs to form an interceptor-style event loop | [Demo](https://demo.thi.ng/umbrella/rstream-event-loop/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/rstream-event-loop) | | ||
| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/todo-list.png" width="240"/> | Obligatory to-do list example with undo/redo | [Demo](https://demo.thi.ng/umbrella/todo-list/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/todo-list) | | ||
| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/triple-query.png" width="240"/> | Triple store query results & sortable table | [Demo](https://demo.thi.ng/umbrella/triple-query/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/triple-query) | | ||
Using hdom in an Elm-like manner | ||
## API | ||
[Live demo](https://demo.thi.ng/umbrella/hdom-elm/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/hdom-elm) | ||
[Generated API docs](https://docs.thi.ng/umbrella/paths/) | ||
### interceptor-basics2 <!-- NOTOC --> | ||
### Type checked paths | ||
Event handling w/ interceptors and side effects | ||
As stated in the [breaking changes](#breaking-changes) section, since | ||
v4.0.0 paths are now type checked by default. These new functions use | ||
Typescript generics to validate a given path against the type structure | ||
of the target state object. Since string paths cannot be checked, only | ||
path tuples are supported. **Type checking & inference supports path | ||
lengths up to 8** (i.e. levels of hierarchy) before reverting back to | ||
`any` for longer/deeper paths (there's no depth limit per se). | ||
[Live demo](https://demo.thi.ng/umbrella/interceptor-basics2/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/interceptor-basics2) | ||
Due to missing type information of the not-yet-known state value, using | ||
the typed checked higher-order versions (e.g. `defGetter`, `defSetter` | ||
etc.) is slightly more verbose compared to their immediate use, | ||
first-order versions (e.g. `getIn()`, `setIn()` etc.), where everything | ||
can be inferred directly. However, (re)using the HOF-constructed | ||
accessors *can* be somewhat faster and more convenient... YMMV! More details below. | ||
### rstream-event-loop <!-- NOTOC --> | ||
#### Optional property handling | ||
![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/rstream-event-loop.png) | ||
When accessing data structures with optional properties, not only the | ||
leaf value type targeted by a lookup path is important, but any | ||
intermediate optional properties need to be considered too. Furthermore, | ||
we need to distinguish between read (get) and write (update) use cases | ||
for correct type inference. | ||
Minimal demo of using rstream constructs to form an interceptor-style event loop | ||
For example, given these types: | ||
[Live demo](https://demo.thi.ng/umbrella/rstream-event-loop/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/rstream-event-loop) | ||
```ts | ||
type Foo1 = { a: { b: { c?: number; } } }; | ||
### todo-list <!-- NOTOC --> | ||
type Foo2 = { a?: { b: { c: number; } } }; | ||
``` | ||
![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/todo-list.png) | ||
For get/read purposes the inferred type for `c` will both be `number | | ||
undefined`. Even though `c` in `Foo2` is not marked as optional, the `a` | ||
property is optional and so attempting to lookup `c` can yield | ||
`undefined`... | ||
Obligatory to-do list example with undo/redo | ||
For set/update/write purposes, the type for `c` is inferred verbatim. | ||
I.e. if a property is marked as optional, a setter will allow | ||
`undefined` as new value as well. | ||
[Live demo](https://demo.thi.ng/umbrella/todo-list/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/todo-list) | ||
### Higher-order accessors | ||
### triple-query <!-- NOTOC --> | ||
The `defGetter()`, `defSetter()` and `defUpdater()` functions compile a | ||
lookup path tuple into an optimized function, operating directly at the | ||
value the path points to in a nested object given later. For getters, | ||
this essentially compiles to: | ||
![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/triple-query.png) | ||
```ts | ||
defGetter(["a","b","c"]) => (obj) => obj.a.b.c; | ||
``` | ||
Triple store query results & sortable table | ||
...with the important difference that the function returns `undefined` | ||
if any intermediate values along the lookup path are undefined (and | ||
doesn't throw an error). | ||
[Live demo](https://demo.thi.ng/umbrella/triple-query/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/triple-query) | ||
For setters / updaters, the resulting function too accepts a single | ||
object (or array) to operate on and when called, **immutably** replaces | ||
the value at the given path, i.e. it produces a selective deep copy of | ||
obj up until given path. If any intermediate key is not present in the | ||
given object, it creates a plain empty object for that missing key and | ||
descends further along the path. | ||
## API | ||
```ts | ||
// define state structure (see above example) | ||
interface State { | ||
a: { | ||
b?: number; | ||
c: string[]; | ||
} | ||
} | ||
[Generated API docs](https://docs.thi.ng/umbrella/paths/) | ||
const state: State = { a: { b: 1, c: ["c1", "c2"] } }; | ||
### Accessors | ||
// build type checked getter for `b` & `c` | ||
const getB = defGetter<State, "a", "b">(["a", "b"]); | ||
const getFirstC = defGetter<State, "a", "c", 0>(["a", "c", 0]); | ||
The `getter()`, `setter()` and `updater()` functions compile a lookup | ||
path like `a.b.c` into an optimized function operating directly at the | ||
value the path points to in nested object. For getters, this essentially | ||
compiles to `val = obj.a.b.c`, with the important difference that the | ||
function returns `undefined` if any intermediate values along the lookup | ||
path are undefined (and doesn't throw an error). | ||
const b = getB(state); // b inferred as `number | undefined` | ||
const c1 = getFirstC(state); // c1 inferred as `string` | ||
``` | ||
The resulting setter function too accepts a single object (or array) to | ||
operate on and when called, **immutably** replaces the value at the | ||
given path, i.e. it produces a selective deep copy of obj up until given | ||
path. If any intermediate key is not present in the given object, it | ||
creates a plain empty object for that missing key and descends further | ||
along the path. | ||
Paths can also be defined as dot-separated strings, however cannot be type checked and MUST use the `Unsafe` version of each operation: | ||
```ts | ||
s = setter("a.b.c"); | ||
// or | ||
s = setter(["a","b","c"]); | ||
s = defSetterUnsafe("a.b.c"); | ||
s({a: {b: {c: 23}}}, 24) | ||
// {a: {b: {c: 24}}} | ||
s({ a: { b: { c: 23 } } }, 24) | ||
// { a: { b: { c: 24 } } } | ||
s({x: 23}, 24) | ||
s({ x: 23 }, 24) | ||
// { x: 23, a: { b: { c: 24 } } } | ||
@@ -137,5 +208,11 @@ | ||
```ts | ||
inc = updater("a.b", (x) => x != null ? x + 1 : 1); | ||
type State = { a?: { b?: number; } }; | ||
inc({a: {b: 10}}); | ||
const inc = defUpdater<State, "a", "b">( | ||
["a","b"], | ||
// x inferred as number | undefined | ||
(x) => x !== undefined ? x + 1 : 1 | ||
); | ||
inc({ a: { b: 10 } }); | ||
// { a: { b: 11 } } | ||
@@ -146,3 +223,3 @@ inc({}); | ||
// with additional arguments | ||
add = updater("a.b", (x, n) => x + n); | ||
add = defUpdater("a.b", (x, n) => x + n); | ||
@@ -153,83 +230,42 @@ add({a: {b: 10}}, 13); | ||
### First order operators | ||
In addition to these higher-order functions, the module also provides | ||
immediate-use wrappers: `getIn()`, `setIn()`, `updateIn()` and | ||
`deleteIn()`. These functions are using `getter` / `setter` internally, | ||
so have same behaviors. | ||
`deleteIn()`. These functions are using `defGetter` / `defSetter` internally, so come with the same contracts/disclaimers... | ||
```ts | ||
state = {a: {b: {c: 23}}}; | ||
const state = { a: { b: { c: 23 } } }; | ||
getIn(state, "a.b.c") | ||
const cPath = <const>["a", "b", "c"]; | ||
getIn(state, cPath) | ||
// 23 | ||
setIn(state, "a.b.c", 24) | ||
// {a: {b: {c: 24}}} | ||
setIn(state, cPath, 24) | ||
// { a: { b: { c: 24 } } } | ||
// apply given function to path value | ||
updateIn(state, "a.b.c", x => x + 1) | ||
// {a: {b: {c: 24}}} | ||
// Note: New `c` is 24, since above `setIn()` didn't mutate orig | ||
updateIn(state, cPath, (x) => x + 1) | ||
// { a: { b: { c: 24 } } } | ||
// immutably remove path key | ||
deleteIn(state, "a.b.c.") | ||
// {a: {b: {}}} | ||
deleteIn(state, cPath) | ||
// { a: { b: {} } } | ||
``` | ||
### Type checked versions | ||
### Deletions | ||
Since v2.2.0 type checked versions of the above accessors are available: | ||
Since `deleteIn` immutably removes a key from the given state object, it | ||
also returns a new type from which the key has been explicitly removed. | ||
Those return types come in the form of `Without{1-8}<...>` interfaces. | ||
- `getterT` / `getInT` | ||
- `setterT` / `setInT` | ||
- `updaterT` / `updateInT` | ||
- `deleteInT` | ||
- `mutatorT` / `mutInT` | ||
These functions use generics (via mapped types) to validate the given | ||
path against the type structure of the state object. Since string paths | ||
cannot be type checked, only path tuples are supported. **Type checking & | ||
inference supports path lengths up to 8** (i.e. levels of | ||
hierarchy) before reverting back to `any`. | ||
```ts | ||
const state = { a: { b: 1, c: ["c1", "c2"] } }; | ||
const b = getInT(state, ["a", "b"]); // b inferred as number | ||
const c = getInT(state, ["a", "c"]); // c inferred as string[] | ||
const c1len = getInT(state, ["a", "c", 0, "length"]); // inferred as number | ||
getIn(state, ["a", "d"]); // compile error | ||
getIn(state, ["x"]); // compile error | ||
``` | ||
Using the typed checked HOF versions (e.g. `getterT`, `setterT` etc.) is | ||
slightly more verbose due to missing type information of the not yet | ||
know state and the way generics are done in TypeScript: | ||
```ts | ||
// define state structure (see above example) | ||
interface State { | ||
a: { | ||
b: number; | ||
c: string[]; | ||
} | ||
} | ||
// build typed getter for `b` & `c` state | ||
const getB = getterT<State, "a", "b">(["a", "b"]); | ||
const getFirstC = getterT<State, "a", "c", 0>(["a", "c", 0]); | ||
// using `state` from previous example | ||
const b = getB(state); // inferred as number | ||
const c1 = getFirstC(state); // inferred as string | ||
``` | ||
Since `deleteInT` immutably removes a key from the given state object, it also returns a new type from which the key has been explicitly removed. | ||
```ts | ||
// again using `state` from above example | ||
// remove nested key `a.c` | ||
const state2 = deleteInT(state, ["a","c"]); | ||
const state2 = deleteIn(state, ["a","b","c"]); | ||
// compile error: "Property `c` does not exist` | ||
state2.a.c; | ||
state2.a.b.c; | ||
``` | ||
@@ -248,8 +284,8 @@ | ||
```ts | ||
s = setter("a.b.c"); | ||
const s = defSetterUnsafe("a.b.c"); | ||
// original | ||
a = { x: { y: { z: 1 } }, u: { v: 2 } }; | ||
const a = { x: { y: { z: 1 } }, u: { v: 2 } }; | ||
// updated version | ||
b = s(a, 3); | ||
const b = s(a, 3); | ||
// { x: { y: { z: 1 } }, u: { v: 2 }, a: { b: { c: 3 } } } | ||
@@ -265,21 +301,21 @@ | ||
`mutator()` is the mutable alternative to `setter()`. It returns a | ||
function, which when called, mutates given object / array at given path | ||
location and bails if any intermediate path values are non-indexable | ||
(only the very last path element can be missing in the actual object | ||
structure). If successful, returns original (mutated) object, else | ||
`undefined`. This function too provides optimized versions for path | ||
lengths <= 4. | ||
`defMutator()`/`defMutatorUnsafe()` are the mutable alternatives to | ||
`defSetter()`/`defSetterUnsafe()`. Each returns a function, which when | ||
called, mutates given object / array at given path location and bails if | ||
any intermediate path values are non-indexable (only the very last path | ||
element can be missing in the actual target object structure). If | ||
successful, returns original (mutated) object, else `undefined`. This | ||
function too provides optimized versions for path lengths <= 4. | ||
As with `setIn`, `mutIn` is the immediate use mutator, i.e. the same as: | ||
`mutator(path)(state, val)`. | ||
`defMutator(path)(state, val)`. | ||
```ts | ||
mutIn({ a: { b: [10, 20] } }, "a.b.1", 23); | ||
mutIn({ a: { b: [10, 20] } }, ["a", "b", 1], 23); | ||
// or | ||
mutIn({ a: { b: [10, 20] } }, ["a", "b", 1], 23); | ||
mutInUnsafe({ a: { b: [10, 20] } }, "a.b.1", 23); | ||
// { a: { b: [ 10, 23 ] } } | ||
// fails (because of missing path structure in target object) | ||
mutIn({}, "a.b.c", 23); | ||
// no-op (because of missing path structure in target object) | ||
mutInUnsafe({}, "a.b.c", 23); | ||
// undefined | ||
@@ -290,6 +326,6 @@ ``` | ||
The `exists()` function takes an arbitrary object and lookup path. | ||
Descends into object along path and returns true if the full path exists | ||
(even if final leaf value is `null` or `undefined`). Checks are | ||
performed using `hasOwnProperty()`. | ||
The `exists()` function takes an arbitrary object and lookup path | ||
(string or tuple). Descends into object along path and returns true if | ||
the full path exists (even if final leaf value is `null` or | ||
`undefined`). Checks are performed using `hasOwnProperty()`. | ||
@@ -296,0 +332,0 @@ ```ts |
@@ -18,2 +18,25 @@ # ${pkg.banner} | ||
## Breaking changes | ||
### 4.0.0 | ||
#### Naming convention | ||
As part of a larger effort to enforce more consistent naming conventions | ||
across various umbrella packages, all higher-order operators in this | ||
package are now using the `def` prefix: e.g. `getterT()` => | ||
`defGetter()`, `setterT()` => `defSetter()`. | ||
#### Type checked accessors | ||
**Type checked accessors are now the default and those functions expect | ||
paths provided as tuples**. To continue using string based paths (e.g. | ||
`"a.b.c"`), alternative `Unsafe` versions are provided. E.g. `getIn()` | ||
(type checked) vs. `getInUnsafe()` (unchecked). Higher-order versions | ||
also provide fallbacks (e.g. `getter()` => `defGetterUnsafe()`). | ||
Type checking for paths is currently "only" supported for the first 8 | ||
levels of nesting. Deeper paths are supported but only partially checked | ||
and their value type inferred as `any`. | ||
${supportPackages} | ||
@@ -43,27 +66,94 @@ | ||
### Accessors | ||
### Type checked paths | ||
The `getter()`, `setter()` and `updater()` functions compile a lookup | ||
path like `a.b.c` into an optimized function operating directly at the | ||
value the path points to in nested object. For getters, this essentially | ||
compiles to `val = obj.a.b.c`, with the important difference that the | ||
function returns `undefined` if any intermediate values along the lookup | ||
path are undefined (and doesn't throw an error). | ||
As stated in the [breaking changes](#breaking-changes) section, since | ||
v4.0.0 paths are now type checked by default. These new functions use | ||
Typescript generics to validate a given path against the type structure | ||
of the target state object. Since string paths cannot be checked, only | ||
path tuples are supported. **Type checking & inference supports path | ||
lengths up to 8** (i.e. levels of hierarchy) before reverting back to | ||
`any` for longer/deeper paths (there's no depth limit per se). | ||
The resulting setter function too accepts a single object (or array) to | ||
operate on and when called, **immutably** replaces the value at the | ||
given path, i.e. it produces a selective deep copy of obj up until given | ||
path. If any intermediate key is not present in the given object, it | ||
creates a plain empty object for that missing key and descends further | ||
along the path. | ||
Due to missing type information of the not-yet-known state value, using | ||
the typed checked higher-order versions (e.g. `defGetter`, `defSetter` | ||
etc.) is slightly more verbose compared to their immediate use, | ||
first-order versions (e.g. `getIn()`, `setIn()` etc.), where everything | ||
can be inferred directly. However, (re)using the HOF-constructed | ||
accessors *can* be somewhat faster and more convenient... YMMV! More details below. | ||
#### Optional property handling | ||
When accessing data structures with optional properties, not only the | ||
leaf value type targeted by a lookup path is important, but any | ||
intermediate optional properties need to be considered too. Furthermore, | ||
we need to distinguish between read (get) and write (update) use cases | ||
for correct type inference. | ||
For example, given these types: | ||
```ts | ||
s = setter("a.b.c"); | ||
// or | ||
s = setter(["a","b","c"]); | ||
type Foo1 = { a: { b: { c?: number; } } }; | ||
s({a: {b: {c: 23}}}, 24) | ||
// {a: {b: {c: 24}}} | ||
type Foo2 = { a?: { b: { c: number; } } }; | ||
``` | ||
s({x: 23}, 24) | ||
For get/read purposes the inferred type for `c` will both be `number | | ||
undefined`. Even though `c` in `Foo2` is not marked as optional, the `a` | ||
property is optional and so attempting to lookup `c` can yield | ||
`undefined`... | ||
For set/update/write purposes, the type for `c` is inferred verbatim. | ||
I.e. if a property is marked as optional, a setter will allow | ||
`undefined` as new value as well. | ||
### Higher-order accessors | ||
The `defGetter()`, `defSetter()` and `defUpdater()` functions compile a | ||
lookup path tuple into an optimized function, operating directly at the | ||
value the path points to in a nested object given later. For getters, | ||
this essentially compiles to: | ||
```ts | ||
defGetter(["a","b","c"]) => (obj) => obj.a.b.c; | ||
``` | ||
...with the important difference that the function returns `undefined` | ||
if any intermediate values along the lookup path are undefined (and | ||
doesn't throw an error). | ||
For setters / updaters, the resulting function too accepts a single | ||
object (or array) to operate on and when called, **immutably** replaces | ||
the value at the given path, i.e. it produces a selective deep copy of | ||
obj up until given path. If any intermediate key is not present in the | ||
given object, it creates a plain empty object for that missing key and | ||
descends further along the path. | ||
```ts | ||
// define state structure (see above example) | ||
interface State { | ||
a: { | ||
b?: number; | ||
c: string[]; | ||
} | ||
} | ||
const state: State = { a: { b: 1, c: ["c1", "c2"] } }; | ||
// build type checked getter for `b` & `c` | ||
const getB = defGetter<State, "a", "b">(["a", "b"]); | ||
const getFirstC = defGetter<State, "a", "c", 0>(["a", "c", 0]); | ||
const b = getB(state); // b inferred as `number | undefined` | ||
const c1 = getFirstC(state); // c1 inferred as `string` | ||
``` | ||
Paths can also be defined as dot-separated strings, however cannot be type checked and MUST use the `Unsafe` version of each operation: | ||
```ts | ||
s = defSetterUnsafe("a.b.c"); | ||
s({ a: { b: { c: 23 } } }, 24) | ||
// { a: { b: { c: 24 } } } | ||
s({ x: 23 }, 24) | ||
// { x: 23, a: { b: { c: 24 } } } | ||
@@ -80,5 +170,11 @@ | ||
```ts | ||
inc = updater("a.b", (x) => x != null ? x + 1 : 1); | ||
type State = { a?: { b?: number; } }; | ||
inc({a: {b: 10}}); | ||
const inc = defUpdater<State, "a", "b">( | ||
["a","b"], | ||
// x inferred as number | undefined | ||
(x) => x !== undefined ? x + 1 : 1 | ||
); | ||
inc({ a: { b: 10 } }); | ||
// { a: { b: 11 } } | ||
@@ -89,3 +185,3 @@ inc({}); | ||
// with additional arguments | ||
add = updater("a.b", (x, n) => x + n); | ||
add = defUpdater("a.b", (x, n) => x + n); | ||
@@ -96,83 +192,42 @@ add({a: {b: 10}}, 13); | ||
### First order operators | ||
In addition to these higher-order functions, the module also provides | ||
immediate-use wrappers: `getIn()`, `setIn()`, `updateIn()` and | ||
`deleteIn()`. These functions are using `getter` / `setter` internally, | ||
so have same behaviors. | ||
`deleteIn()`. These functions are using `defGetter` / `defSetter` internally, so come with the same contracts/disclaimers... | ||
```ts | ||
state = {a: {b: {c: 23}}}; | ||
const state = { a: { b: { c: 23 } } }; | ||
getIn(state, "a.b.c") | ||
const cPath = <const>["a", "b", "c"]; | ||
getIn(state, cPath) | ||
// 23 | ||
setIn(state, "a.b.c", 24) | ||
// {a: {b: {c: 24}}} | ||
setIn(state, cPath, 24) | ||
// { a: { b: { c: 24 } } } | ||
// apply given function to path value | ||
updateIn(state, "a.b.c", x => x + 1) | ||
// {a: {b: {c: 24}}} | ||
// Note: New `c` is 24, since above `setIn()` didn't mutate orig | ||
updateIn(state, cPath, (x) => x + 1) | ||
// { a: { b: { c: 24 } } } | ||
// immutably remove path key | ||
deleteIn(state, "a.b.c.") | ||
// {a: {b: {}}} | ||
deleteIn(state, cPath) | ||
// { a: { b: {} } } | ||
``` | ||
### Type checked versions | ||
### Deletions | ||
Since v2.2.0 type checked versions of the above accessors are available: | ||
Since `deleteIn` immutably removes a key from the given state object, it | ||
also returns a new type from which the key has been explicitly removed. | ||
Those return types come in the form of `Without{1-8}<...>` interfaces. | ||
- `getterT` / `getInT` | ||
- `setterT` / `setInT` | ||
- `updaterT` / `updateInT` | ||
- `deleteInT` | ||
- `mutatorT` / `mutInT` | ||
These functions use generics (via mapped types) to validate the given | ||
path against the type structure of the state object. Since string paths | ||
cannot be type checked, only path tuples are supported. **Type checking & | ||
inference supports path lengths up to 8** (i.e. levels of | ||
hierarchy) before reverting back to `any`. | ||
```ts | ||
const state = { a: { b: 1, c: ["c1", "c2"] } }; | ||
const b = getInT(state, ["a", "b"]); // b inferred as number | ||
const c = getInT(state, ["a", "c"]); // c inferred as string[] | ||
const c1len = getInT(state, ["a", "c", 0, "length"]); // inferred as number | ||
getIn(state, ["a", "d"]); // compile error | ||
getIn(state, ["x"]); // compile error | ||
``` | ||
Using the typed checked HOF versions (e.g. `getterT`, `setterT` etc.) is | ||
slightly more verbose due to missing type information of the not yet | ||
know state and the way generics are done in TypeScript: | ||
```ts | ||
// define state structure (see above example) | ||
interface State { | ||
a: { | ||
b: number; | ||
c: string[]; | ||
} | ||
} | ||
// build typed getter for `b` & `c` state | ||
const getB = getterT<State, "a", "b">(["a", "b"]); | ||
const getFirstC = getterT<State, "a", "c", 0>(["a", "c", 0]); | ||
// using `state` from previous example | ||
const b = getB(state); // inferred as number | ||
const c1 = getFirstC(state); // inferred as string | ||
``` | ||
Since `deleteInT` immutably removes a key from the given state object, it also returns a new type from which the key has been explicitly removed. | ||
```ts | ||
// again using `state` from above example | ||
// remove nested key `a.c` | ||
const state2 = deleteInT(state, ["a","c"]); | ||
const state2 = deleteIn(state, ["a","b","c"]); | ||
// compile error: "Property `c` does not exist` | ||
state2.a.c; | ||
state2.a.b.c; | ||
``` | ||
@@ -191,8 +246,8 @@ | ||
```ts | ||
s = setter("a.b.c"); | ||
const s = defSetterUnsafe("a.b.c"); | ||
// original | ||
a = { x: { y: { z: 1 } }, u: { v: 2 } }; | ||
const a = { x: { y: { z: 1 } }, u: { v: 2 } }; | ||
// updated version | ||
b = s(a, 3); | ||
const b = s(a, 3); | ||
// { x: { y: { z: 1 } }, u: { v: 2 }, a: { b: { c: 3 } } } | ||
@@ -208,21 +263,21 @@ | ||
`mutator()` is the mutable alternative to `setter()`. It returns a | ||
function, which when called, mutates given object / array at given path | ||
location and bails if any intermediate path values are non-indexable | ||
(only the very last path element can be missing in the actual object | ||
structure). If successful, returns original (mutated) object, else | ||
`undefined`. This function too provides optimized versions for path | ||
lengths <= 4. | ||
`defMutator()`/`defMutatorUnsafe()` are the mutable alternatives to | ||
`defSetter()`/`defSetterUnsafe()`. Each returns a function, which when | ||
called, mutates given object / array at given path location and bails if | ||
any intermediate path values are non-indexable (only the very last path | ||
element can be missing in the actual target object structure). If | ||
successful, returns original (mutated) object, else `undefined`. This | ||
function too provides optimized versions for path lengths <= 4. | ||
As with `setIn`, `mutIn` is the immediate use mutator, i.e. the same as: | ||
`mutator(path)(state, val)`. | ||
`defMutator(path)(state, val)`. | ||
```ts | ||
mutIn({ a: { b: [10, 20] } }, "a.b.1", 23); | ||
mutIn({ a: { b: [10, 20] } }, ["a", "b", 1], 23); | ||
// or | ||
mutIn({ a: { b: [10, 20] } }, ["a", "b", 1], 23); | ||
mutInUnsafe({ a: { b: [10, 20] } }, "a.b.1", 23); | ||
// { a: { b: [ 10, 23 ] } } | ||
// fails (because of missing path structure in target object) | ||
mutIn({}, "a.b.c", 23); | ||
// no-op (because of missing path structure in target object) | ||
mutInUnsafe({}, "a.b.c", 23); | ||
// undefined | ||
@@ -233,6 +288,6 @@ ``` | ||
The `exists()` function takes an arbitrary object and lookup path. | ||
Descends into object along path and returns true if the full path exists | ||
(even if final leaf value is `null` or `undefined`). Checks are | ||
performed using `hasOwnProperty()`. | ||
The `exists()` function takes an arbitrary object and lookup path | ||
(string or tuple). Descends into object along path and returns true if | ||
the full path exists (even if final leaf value is `null` or | ||
`undefined`). Checks are performed using `hasOwnProperty()`. | ||
@@ -239,0 +294,0 @@ ```ts |
@@ -0,14 +1,24 @@ | ||
import type { Path } from "@thi.ng/api"; | ||
/** | ||
* Similar to {@link setIn}, but takes any number of path-value pairs as | ||
* args and applies them in sequence by calling `setIn()` for each. | ||
* Similar to {@link setInUnsafe}, but takes any number of path-value | ||
* pairs as args and applies them in sequence by calling `setIn()` for | ||
* each. | ||
* | ||
* @remarks | ||
* Any key paths missing in the data structure will be created. Does | ||
* *not* mutate original (instead use {@link mutInMany} for this | ||
* purpose). | ||
* Any intermediate key paths missing in the data structure will be | ||
* created. Does NOT mutate original (instead use | ||
* {@link mutInManyUnsafe} for this purpose). | ||
* | ||
* Unlike {@link setIn}, this function does not use type checked paths. | ||
* The type parameter `T` can be used to indicate the type of the | ||
* overall state value and will also be used as return type. | ||
* | ||
* ``` | ||
* setInMany({}, "a.b", 10, "x.y.z", 20) | ||
* @example | ||
* ```ts | ||
* setInManyUnsafe( | ||
* {}, | ||
* // pair #1 | ||
* "a.b", 10, | ||
* // pair #2 | ||
* "x.y.z", 20 | ||
* ); | ||
* // { a: { b: 10 }, x: { y: { z: 20 } } } | ||
@@ -20,3 +30,7 @@ * ``` | ||
*/ | ||
export declare const setInMany: (state: any, ...pairs: any[]) => any; | ||
export declare function setInManyUnsafe<T>(state: T, p1: Path, v1: any): T; | ||
export declare function setInManyUnsafe<T>(state: T, p1: Path, v1: any, p2: Path, v2: any): T; | ||
export declare function setInManyUnsafe<T>(state: T, p1: Path, v1: any, p2: Path, v2: any, p3: Path, v3: any): T; | ||
export declare function setInManyUnsafe<T>(state: T, p1: Path, v1: any, p2: Path, v2: any, p3: Path, v3: any, p4: Path, v4: any): T; | ||
export declare function setInManyUnsafe<T>(state: T, p1: Path, v1: any, p2: Path, v2: any, p3: Path, v3: any, p4: Path, v4: any, ...xs: any[]): T; | ||
//# sourceMappingURL=set-in-many.d.ts.map |
import { illegalArgs } from "@thi.ng/errors"; | ||
import { setInT } from "./set-in"; | ||
/** | ||
* Similar to {@link setIn}, but takes any number of path-value pairs as | ||
* args and applies them in sequence by calling `setIn()` for each. | ||
* | ||
* @remarks | ||
* Any key paths missing in the data structure will be created. Does | ||
* *not* mutate original (instead use {@link mutInMany} for this | ||
* purpose). | ||
* | ||
* Unlike {@link setIn}, this function does not use type checked paths. | ||
* | ||
* ``` | ||
* setInMany({}, "a.b", 10, "x.y.z", 20) | ||
* // { a: { b: 10 }, x: { y: { z: 20 } } } | ||
* ``` | ||
* | ||
* @param state - | ||
* @param pairs - | ||
*/ | ||
export const setInMany = (state, ...pairs) => { | ||
import { setIn } from "./set-in"; | ||
export function setInManyUnsafe(state, ...pairs) { | ||
const n = pairs.length; | ||
@@ -27,5 +8,5 @@ n & 1 && | ||
for (let i = 0; i < n; i += 2) { | ||
state = setInT(state, pairs[i], pairs[i + 1]); | ||
state = setIn(state, pairs[i], pairs[i + 1]); | ||
} | ||
return state; | ||
}; | ||
} |
@@ -1,13 +0,12 @@ | ||
import type { Keys, Keys1, Keys2, Keys3, Keys4, Keys5, Keys6, Keys7, Path, Val1, Val2, Val3, Val4, Val5, Val6, Val7, Val8 } from "@thi.ng/api"; | ||
import type { DeepPath, Path, Path0, Path1, Path2, Path3, Path4, Path5, Path6, Path7, Path8, PathVal } from "@thi.ng/api"; | ||
/** | ||
* Immediate use setter, i.e. same as: `setter(path)(state, val)`. | ||
* Unchecked version of {@link setIn}. | ||
* | ||
* @remarks | ||
* Supports type checked paths and values for path lengths <= 8. String | ||
* paths are always unchecked (i.e. `state` is `any`). | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be set (default: `any`). | ||
* | ||
* @example | ||
* ```ts | ||
* | ||
* setIn({}, "a.b.c", 23); | ||
* setInUnsafe({}, "a.b.c", 23); | ||
* // { a: { b: { c: 23} } } | ||
@@ -19,6 +18,20 @@ * ``` | ||
*/ | ||
export declare const setIn: (state: any, path: Path, val: any) => any; | ||
export declare const setInUnsafe: <T>(state: any, path: Path, val: T) => any; | ||
/** | ||
* Type checked version of {@link setIn}. | ||
* Type checked, immediate use setter, i.e. same as: | ||
* `defSetterUnsafe(path)(state, val)`. | ||
* | ||
* @remarks | ||
* Only the first 8 path levels are type checked. | ||
* | ||
* @example | ||
* ```ts | ||
* // type checked path & value | ||
* setIn({ a: { b: { c: 23 } } }, ["a", "b", "c"], 24); | ||
* // { a: { b: { c: 24 } } } | ||
* | ||
* // error (wrong value type) | ||
* setIn({ a: { b: { c: 23 } } }, ["a", "b", "c"], "24"); | ||
* ``` | ||
* | ||
* @param state - | ||
@@ -28,12 +41,12 @@ * @param path - | ||
*/ | ||
export declare function setInT<T>(state: T, path: [], val: T): T; | ||
export declare function setInT<T, A extends Keys<T>>(state: T, path: [A], val: Val1<T, A>): T; | ||
export declare function setInT<T, A extends Keys<T>, B extends Keys1<T, A>>(state: T, path: [A, B], val: Val2<T, A, B>): T; | ||
export declare function setInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>>(state: T, path: [A, B, C], val: Val3<T, A, B, C>): T; | ||
export declare function setInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>>(state: T, path: [A, B, C, D], val: Val4<T, A, B, C, D>): T; | ||
export declare function setInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>>(state: T, path: [A, B, C, D, E], val: Val5<T, A, B, C, D, E>): T; | ||
export declare function setInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>>(state: T, path: [A, B, C, D, E, F], val: Val6<T, A, B, C, D, E, F>): T; | ||
export declare function setInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>>(state: T, path: [A, B, C, D, E, F, G], val: Val7<T, A, B, C, D, E, F, G>): T; | ||
export declare function setInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(state: T, path: [A, B, C, D, E, F, G, H], val: Val8<T, A, B, C, D, E, F, G, H>): T; | ||
export declare function setInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(state: T, path: [A, B, C, D, E, F, G, H, ...PropertyKey[]], val: any): any; | ||
export declare function setIn<T>(state: T, path: Path0, val: T): T; | ||
export declare function setIn<T, A>(state: T, path: Path1<T, A>, val: PathVal<T, [A]>): T; | ||
export declare function setIn<T, A, B>(state: T, path: Path2<T, A, B>, val: PathVal<T, [A, B]>): T; | ||
export declare function setIn<T, A, B, C>(state: T, path: Path3<T, A, B, C>, val: PathVal<T, [A, B, C]>): T; | ||
export declare function setIn<T, A, B, C, D>(state: T, path: Path4<T, A, B, C, D>, val: PathVal<T, [A, B, C, D]>): T; | ||
export declare function setIn<T, A, B, C, D, E>(state: T, path: Path5<T, A, B, C, D, E>, val: PathVal<T, [A, B, C, D, E]>): T; | ||
export declare function setIn<T, A, B, C, D, E, F>(state: T, path: Path6<T, A, B, C, D, E, F>, val: PathVal<T, [A, B, C, D, E, F]>): T; | ||
export declare function setIn<T, A, B, C, D, E, F, G>(state: T, path: Path7<T, A, B, C, D, E, F, G>, val: PathVal<T, [A, B, C, D, E, F, G]>): T; | ||
export declare function setIn<T, A, B, C, D, E, F, G, H>(state: T, path: Path8<T, A, B, C, D, E, F, G, H>, val: PathVal<T, [A, B, C, D, E, F, G, H]>): T; | ||
export declare function setIn<T, A, B, C, D, E, F, G, H>(state: T, path: DeepPath<T, A, B, C, D, E, F, G, H>, val: any): any; | ||
//# sourceMappingURL=set-in.d.ts.map |
@@ -1,13 +0,12 @@ | ||
import { setterT } from "./setter"; | ||
import { defSetter } from "./setter"; | ||
/** | ||
* Immediate use setter, i.e. same as: `setter(path)(state, val)`. | ||
* Unchecked version of {@link setIn}. | ||
* | ||
* @remarks | ||
* Supports type checked paths and values for path lengths <= 8. String | ||
* paths are always unchecked (i.e. `state` is `any`). | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be set (default: `any`). | ||
* | ||
* @example | ||
* ```ts | ||
* | ||
* setIn({}, "a.b.c", 23); | ||
* setInUnsafe({}, "a.b.c", 23); | ||
* // { a: { b: { c: 23} } } | ||
@@ -19,5 +18,5 @@ * ``` | ||
*/ | ||
export const setIn = (state, path, val) => setterT(path)(state, val); | ||
export function setInT(state, path, val) { | ||
return setterT(path)(state, val); | ||
export const setInUnsafe = (state, path, val) => defSetter(path)(state, val); | ||
export function setIn(state, path, val) { | ||
return defSetter(path)(state, val); | ||
} |
@@ -1,2 +0,2 @@ | ||
import type { Fn2, Keys, Keys1, Keys2, Keys3, Keys4, Keys5, Keys6, Keys7, Path, Val1, Val2, Val3, Val4, Val5, Val6, Val7, Val8 } from "@thi.ng/api"; | ||
import type { DeepPath, Fn2, Path, Path0, Path1, Path2, Path3, Path4, Path5, Path6, Path7, Path8, PathVal } from "@thi.ng/api"; | ||
/** | ||
@@ -12,2 +12,5 @@ * Composes a setter function for given nested update path. Optimized | ||
* | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be updated (default: `any`). | ||
* | ||
* If `path` is given as string, it will be split using `.`. Returns | ||
@@ -28,9 +31,7 @@ * function which accepts single object and when called, **immutably** | ||
* | ||
* Also see: {@link setIn}, {@link updateIn}, {@link deleteIn} | ||
* | ||
* @example | ||
* ``` | ||
* s = setter("a.b.c"); | ||
* ```ts | ||
* s = defSetterUnsafe("a.b.c"); | ||
* // or | ||
* s = setter(["a","b","c"]); | ||
* s = defSetterUnsafe(["a", "b", "c"]); | ||
* | ||
@@ -49,5 +50,5 @@ * s({ a: { b: { c: 23} } }, 24) | ||
* ```ts | ||
* s = setter("a.b.c"); | ||
* s = defSetterUnsafe("a.b.c"); | ||
* | ||
* a = {x: {y: {z: 1}}}; | ||
* a = { x: { y: { z: 1 } } }; | ||
* b = s(a, 2); | ||
@@ -62,18 +63,49 @@ * // { x: { y: { z: 1 } }, a: { b: { c: 2 } } } | ||
*/ | ||
export declare const setter: (path: Path) => Fn2<any, any, any>; | ||
export declare const defSetterUnsafe: <T = any>(path: Path) => Fn2<any, T, any>; | ||
/** | ||
* Type checked version of {@link setter}. | ||
* Type checked version of {@link defSetterUnsafe}. Only the first 8 | ||
* path levels are type checked. | ||
* | ||
* @remarks | ||
* Due to the higher-order nature of this function, generics for path | ||
* validation must be given and so this function is more verbose than | ||
* {@link setIn} (where the generics can usually be fully inferred). | ||
* | ||
* @example | ||
* ```ts | ||
* type State = { a: { b: number } }; | ||
* | ||
* const setB = defSetter<State, "a", "b">(["a", "b"]); | ||
* | ||
* setB({ a: { b: 1 } }, 2); // ok! | ||
* setB({ a: { b: 1 } }, "2"); // error! | ||
* ``` | ||
* | ||
* @example | ||
* ```ts | ||
* type State = { a: { b: number } }; | ||
* | ||
* const path = <const>["a","b"]; | ||
* | ||
* const setB = defSetter<State, typeof path[0], typeof path[1]>(path); | ||
* ``` | ||
* | ||
* @param path - | ||
*/ | ||
export declare function setterT<T>(path: []): Fn2<T, T, T>; | ||
export declare function setterT<T, A extends Keys<T>>(path: [A]): Fn2<T, Val1<T, A>, T>; | ||
export declare function setterT<T, A extends Keys<T>, B extends Keys1<T, A>>(path: [A, B]): Fn2<T, Val2<T, A, B>, T>; | ||
export declare function setterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>>(path: [A, B, C]): Fn2<T, Val3<T, A, B, C>, T>; | ||
export declare function setterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>>(path: [A, B, C, D]): Fn2<T, Val4<T, A, B, C, D>, T>; | ||
export declare function setterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>>(path: [A, B, C, D, E]): Fn2<T, Val5<T, A, B, C, D, E>, T>; | ||
export declare function setterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>>(path: [A, B, C, D, E, F]): Fn2<T, Val6<T, A, B, C, D, E, F>, T>; | ||
export declare function setterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>>(path: [A, B, C, D, E, F, G]): Fn2<T, Val7<T, A, B, C, D, E, F, G>, T>; | ||
export declare function setterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(path: [A, B, C, D, E, F, G, H]): Fn2<T, Val8<T, A, B, C, D, E, F, G, H>, T>; | ||
export declare function setterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(path: [A, B, C, D, E, F, G, H, ...PropertyKey[]]): Fn2<T, any, any>; | ||
export declare function defSetter<T>(path: Path0): Fn2<T, T, T>; | ||
export declare function defSetter<T, A>(path: Path1<T, A>): Fn2<T, PathVal<T, [A]>, T>; | ||
export declare function defSetter<T, A, B>(path: Path2<T, A, B>): Fn2<T, PathVal<T, [A, B]>, T>; | ||
export declare function defSetter<T, A, B, C>(path: Path3<T, A, B, C>): Fn2<T, PathVal<T, [A, B, C]>, T>; | ||
export declare function defSetter<T, A, B, C, D>(path: Path4<T, A, B, C, D>): Fn2<T, PathVal<T, [A, B, C, D]>, T>; | ||
export declare function defSetter<T, A, B, C, D, E>(path: Path5<T, A, B, C, D, E>): Fn2<T, PathVal<T, [A, B, C, D, E]>, T>; | ||
export declare function defSetter<T, A, B, C, D, E, F>(path: Path6<T, A, B, C, D, E, F>): Fn2<T, PathVal<T, [A, B, C, D, E, F]>, T>; | ||
export declare function defSetter<T, A, B, C, D, E, F, G>(path: Path7<T, A, B, C, D, E, F, G>): Fn2<T, PathVal<T, [A, B, C, D, E, F, G]>, T>; | ||
export declare function defSetter<T, A, B, C, D, E, F, G, H>(path: Path8<T, A, B, C, D, E, F, G, H>): Fn2<T, PathVal<T, [A, B, C, D, E, F, G, H]>, T>; | ||
export declare function defSetter<T, A, B, C, D, E, F, G, H>(path: DeepPath<T, A, B, C, D, E, F, G, H>): Fn2<T, any, any>; | ||
/** | ||
* Creates a shallow copy of given array, typed array or plain object. | ||
* | ||
* @param x | ||
*/ | ||
export declare const copy: (x: any) => any; | ||
//# sourceMappingURL=setter.d.ts.map |
@@ -1,2 +0,2 @@ | ||
import { isArray } from "@thi.ng/checks"; | ||
import { isArray, isTypedArray } from "@thi.ng/checks"; | ||
import { toPath } from "./path"; | ||
@@ -13,2 +13,5 @@ /** | ||
* | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be updated (default: `any`). | ||
* | ||
* If `path` is given as string, it will be split using `.`. Returns | ||
@@ -29,9 +32,7 @@ * function which accepts single object and when called, **immutably** | ||
* | ||
* Also see: {@link setIn}, {@link updateIn}, {@link deleteIn} | ||
* | ||
* @example | ||
* ``` | ||
* s = setter("a.b.c"); | ||
* ```ts | ||
* s = defSetterUnsafe("a.b.c"); | ||
* // or | ||
* s = setter(["a","b","c"]); | ||
* s = defSetterUnsafe(["a", "b", "c"]); | ||
* | ||
@@ -50,5 +51,5 @@ * s({ a: { b: { c: 23} } }, 24) | ||
* ```ts | ||
* s = setter("a.b.c"); | ||
* s = defSetterUnsafe("a.b.c"); | ||
* | ||
* a = {x: {y: {z: 1}}}; | ||
* a = { x: { y: { z: 1 } } }; | ||
* b = s(a, 2); | ||
@@ -63,6 +64,6 @@ * // { x: { y: { z: 1 } }, a: { b: { c: 2 } } } | ||
*/ | ||
export const setter = (path) => setterT(path); | ||
export function setterT(path) { | ||
export const defSetterUnsafe = (path) => defSetter(path); | ||
export function defSetter(path) { | ||
const ks = toPath(path); | ||
let [a, b, c, d] = ks; | ||
const [a, b, c, d] = ks; | ||
switch (ks.length) { | ||
@@ -72,8 +73,8 @@ case 0: | ||
case 1: | ||
return (s, v) => ((s = _copy(s)), (s[a] = v), s); | ||
return (s, v) => ((s = copy(s)), (s[a] = v), s); | ||
case 2: | ||
return (s, v) => { | ||
let x; | ||
s = _copy(s); | ||
s[a] = x = _copy(s[a]); | ||
s = copy(s); | ||
s[a] = x = copy(s[a]); | ||
x[b] = v; | ||
@@ -85,5 +86,5 @@ return s; | ||
let x, y; | ||
s = _copy(s); | ||
s[a] = x = _copy(s[a]); | ||
x[b] = y = _copy(x[b]); | ||
s = copy(s); | ||
s[a] = x = copy(s[a]); | ||
x[b] = y = copy(x[b]); | ||
y[c] = v; | ||
@@ -95,6 +96,6 @@ return s; | ||
let x, y, z; | ||
s = _copy(s); | ||
s[a] = x = _copy(s[a]); | ||
x[b] = y = _copy(x[b]); | ||
y[c] = z = _copy(y[c]); | ||
s = copy(s); | ||
s[a] = x = copy(s[a]); | ||
x[b] = y = copy(x[b]); | ||
y[c] = z = copy(y[c]); | ||
z[d] = v; | ||
@@ -111,3 +112,16 @@ return s; | ||
} | ||
const _copy = (s) => (isArray(s) ? s.slice() : Object.assign({}, s)); | ||
const compS = (k, f) => (s, v) => ((s = _copy(s)), (s[k] = f ? f(s[k], v) : v), s); | ||
/** | ||
* Creates a shallow copy of given array, typed array or plain object. | ||
* | ||
* @param x | ||
*/ | ||
export const copy = (x) => isArray(x) || isTypedArray(x) ? x.slice() : Object.assign({}, x); | ||
/** | ||
* Helper for {@link defSetter}. Returns setter for a single step. | ||
* | ||
* @param k - | ||
* @param f - | ||
* | ||
* @internal | ||
*/ | ||
const compS = (k, f) => (s, v) => ((s = copy(s)), (s[k] = f ? f(s[k], v) : v), s); |
@@ -1,13 +0,26 @@ | ||
import type { Keys, Keys1, Keys2, Keys3, Keys4, Keys5, Keys6, Keys7, Path, Val1, Val2, Val3, Val4, Val5, Val6, Val7, Val8 } from "@thi.ng/api"; | ||
import type { DeepPath, Path, Path0, Path1, Path2, Path3, Path4, Path5, Path6, Path7, Path8, OptPathVal, PathVal } from "@thi.ng/api"; | ||
import type { UpdateFn } from "./api"; | ||
/** | ||
* Similar to {@link setIn}, but applies given function to current path | ||
* value (incl. any additional/optional arguments passed to `updateIn`) | ||
* and uses result as new value. Does not modify original state (unless | ||
* given function does so itself). | ||
* Similar to {@link setInUnsafe}, but applies given function to current | ||
* path value (incl. any additional/optional arguments passed to | ||
* `updateIn`) and uses result as new value. Does not modify original | ||
* state. | ||
* | ||
* ``` | ||
* add = (x, y) => x + y; | ||
* updateIn({ a: { b: { c: 23 } } }, "a.b.c", add, 10); | ||
* @remarks | ||
* Unchecked version of {@link updateIn}. The type parameter `T` can be | ||
* used to indicate the type of the nested value to be updated (default: | ||
* `any`). | ||
* | ||
* @example | ||
* ```ts | ||
* add = (x: number, y: number) => x + y; | ||
* updateInUnsafe({ a: { b: { c: 23 } } }, "a.b.c", add, 10); | ||
* // { a: { b: { c: 33 } } } | ||
* | ||
* // type checked | ||
* updateIn({ a: { b: { c: 23 } } }, ["a","b","c"], add, 10); | ||
* // { a: { b: { c: 33 } } } | ||
* | ||
* // type error (value at "a.b" is not a number) | ||
* updateIn({ a: { b: { c: 23 } } }, ["a","b"], add, 10); | ||
* ``` | ||
@@ -20,5 +33,6 @@ * | ||
*/ | ||
export declare const updateIn: (state: any, path: Path, fn: UpdateFn<any>, ...args: any[]) => any; | ||
export declare const updateInUnsafe: <T = any>(state: any, path: Path, fn: import("@thi.ng/api").FnO<T, T>, ...args: any[]) => any; | ||
/** | ||
* Type checked version of {@link updateIn}. | ||
* Type checked version of {@link updateIn}. Only the first 8 path | ||
* levels are type checked. | ||
* | ||
@@ -29,12 +43,12 @@ * @param state - | ||
*/ | ||
export declare function updateInT<T>(state: T, path: [], fn: UpdateFn<T>): T; | ||
export declare function updateInT<T, A extends Keys<T>>(state: T, path: [A], fn: UpdateFn<Val1<T, A>>, ...args: any[]): T; | ||
export declare function updateInT<T, A extends Keys<T>, B extends Keys1<T, A>>(state: T, path: [A, B], fn: UpdateFn<Val2<T, A, B>>, ...args: any[]): T; | ||
export declare function updateInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>>(state: T, path: [A, B, C], fn: UpdateFn<Val3<T, A, B, C>>, ...args: any[]): T; | ||
export declare function updateInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>>(state: T, path: [A, B, C, D], fn: UpdateFn<Val4<T, A, B, C, D>>, ...args: any[]): T; | ||
export declare function updateInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>>(state: T, path: [A, B, C, D, E], fn: UpdateFn<Val5<T, A, B, C, D, E>>, ...args: any[]): T; | ||
export declare function updateInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>>(state: T, path: [A, B, C, D, E, F], fn: UpdateFn<Val6<T, A, B, C, D, E, F>>, ...args: any[]): T; | ||
export declare function updateInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>>(state: T, path: [A, B, C, D, E, F, G], fn: UpdateFn<Val7<T, A, B, C, D, E, F, G>>, ...args: any[]): T; | ||
export declare function updateInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(state: T, path: [A, B, C, D, E, F, G, H], fn: UpdateFn<Val8<T, A, B, C, D, E, F, G, H>>, ...args: any[]): T; | ||
export declare function updateInT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(state: T, path: [A, B, C, D, E, F, G, H, ...PropertyKey[]], fn: UpdateFn<any>, ...args: any[]): T; | ||
export declare function updateIn<T>(state: T, path: Path0, fn: UpdateFn<T, T>): T; | ||
export declare function updateIn<T, A>(state: T, path: Path1<T, A>, fn: UpdateFn<OptPathVal<T, [A]>, PathVal<T, [A]>>, ...args: any[]): T; | ||
export declare function updateIn<T, A, B>(state: T, path: Path2<T, A, B>, fn: UpdateFn<OptPathVal<T, [A, B]>, PathVal<T, [A, B]>>, ...args: any[]): T; | ||
export declare function updateIn<T, A, B, C>(state: T, path: Path3<T, A, B, C>, fn: UpdateFn<OptPathVal<T, [A, B, C]>, PathVal<T, [A, B, C]>>, ...args: any[]): T; | ||
export declare function updateIn<T, A, B, C, D>(state: T, path: Path4<T, A, B, C, D>, fn: UpdateFn<OptPathVal<T, [A, B, C, D]>, PathVal<T, [A, B, C, D]>>, ...args: any[]): T; | ||
export declare function updateIn<T, A, B, C, D, E>(state: T, path: Path5<T, A, B, C, D, E>, fn: UpdateFn<OptPathVal<T, [A, B, C, D, E]>, PathVal<T, [A, B, C, D, E]>>, ...args: any[]): T; | ||
export declare function updateIn<T, A, B, C, D, E, F>(state: T, path: Path6<T, A, B, C, D, E, F>, fn: UpdateFn<OptPathVal<T, [A, B, C, D, E, F]>, PathVal<T, [A, B, C, D, E, F]>>, ...args: any[]): T; | ||
export declare function updateIn<T, A, B, C, D, E, F, G>(state: T, path: Path7<T, A, B, C, D, E, F, G>, fn: UpdateFn<OptPathVal<T, [A, B, C, D, E, F, G]>, PathVal<T, [A, B, C, D, E, F, G]>>, ...args: any[]): T; | ||
export declare function updateIn<T, A, B, C, D, E, F, G, H>(state: T, path: Path8<T, A, B, C, D, E, F, G, H>, fn: UpdateFn<OptPathVal<T, [A, B, C, D, E, F, G, H]>, PathVal<T, [A, B, C, D, E, F, G, H]>>, ...args: any[]): T; | ||
export declare function updateIn<T, A, B, C, D, E, F, G, H>(state: T, path: DeepPath<T, A, B, C, D, E, F, G, H>, fn: UpdateFn<any, any>, ...args: any[]): T; | ||
//# sourceMappingURL=update-in.d.ts.map |
@@ -1,13 +0,26 @@ | ||
import { getterT } from "./getter"; | ||
import { setterT } from "./setter"; | ||
import { defGetter } from "./getter"; | ||
import { defSetter } from "./setter"; | ||
/** | ||
* Similar to {@link setIn}, but applies given function to current path | ||
* value (incl. any additional/optional arguments passed to `updateIn`) | ||
* and uses result as new value. Does not modify original state (unless | ||
* given function does so itself). | ||
* Similar to {@link setInUnsafe}, but applies given function to current | ||
* path value (incl. any additional/optional arguments passed to | ||
* `updateIn`) and uses result as new value. Does not modify original | ||
* state. | ||
* | ||
* ``` | ||
* add = (x, y) => x + y; | ||
* updateIn({ a: { b: { c: 23 } } }, "a.b.c", add, 10); | ||
* @remarks | ||
* Unchecked version of {@link updateIn}. The type parameter `T` can be | ||
* used to indicate the type of the nested value to be updated (default: | ||
* `any`). | ||
* | ||
* @example | ||
* ```ts | ||
* add = (x: number, y: number) => x + y; | ||
* updateInUnsafe({ a: { b: { c: 23 } } }, "a.b.c", add, 10); | ||
* // { a: { b: { c: 33 } } } | ||
* | ||
* // type checked | ||
* updateIn({ a: { b: { c: 23 } } }, ["a","b","c"], add, 10); | ||
* // { a: { b: { c: 33 } } } | ||
* | ||
* // type error (value at "a.b" is not a number) | ||
* updateIn({ a: { b: { c: 23 } } }, ["a","b"], add, 10); | ||
* ``` | ||
@@ -20,5 +33,9 @@ * | ||
*/ | ||
export const updateIn = (state, path, fn, ...args) => updateInT(state, path, fn, ...args); | ||
export function updateInT(state, path, fn, ...args) { | ||
return setterT(path)(state, fn.apply(null, (args.unshift(getterT(path)(state)), args))); | ||
export const updateInUnsafe = (state, path, fn, ...args) => | ||
// @ts-ignore | ||
updateIn(state, path, fn, ...args); | ||
export function updateIn(state, path, fn, ...args) { | ||
return defSetter(path)(state, | ||
// @ts-ignore | ||
fn.apply(null, (args.unshift(defGetter(path)(state)), args))); | ||
} |
@@ -1,17 +0,23 @@ | ||
import type { FnO, Keys, Keys1, Keys2, Keys3, Keys4, Keys5, Keys6, Keys7, Path, Val1, Val2, Val3, Val4, Val5, Val6, Val7, Val8 } from "@thi.ng/api"; | ||
import type { DeepPath, FnO, Path, Path0, Path1, Path2, Path3, Path4, Path5, Path6, Path7, Path8, OptPathVal, PathVal } from "@thi.ng/api"; | ||
import type { UpdateFn } from "./api"; | ||
/** | ||
* Similar to {@link setter}, returns a function to update values at | ||
* given `path` using provided update `fn`. | ||
* Similar to {@link defSetterUnsafe}, returns a function to update | ||
* values at given `path` using provided update `fn`. Paths and the | ||
* arguments given to the returned function are NOT type checked. | ||
* | ||
* @remarks | ||
* The returned function accepts a single object / array and applies | ||
* `fn` to current path value (incl. any additional/optional arguments | ||
* passed) and uses result as new value. Does not modify original state | ||
* (unless given function does so itself). | ||
* `fn` to given path value (incl. any additional / optional arguments | ||
* passed) and uses result as new value. Does not modify original state. | ||
* | ||
* ``` | ||
* add = updater("a.b", (x, n) => x + n); | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be updated (default: `any`). | ||
* | ||
* add({a: {b: 10}}, 13); | ||
* @example | ||
* ```ts | ||
* const incB = defUpdaterUnsafe("a.b", (x, n) => x + n); | ||
* // or | ||
* const incB = defUpdaterUnsafe(["a", "b"], (x, n) => x + n); | ||
* | ||
* incB({ a: { b: 10 } }, 13); | ||
* // { a: { b: 23 } } | ||
@@ -23,5 +29,6 @@ * ``` | ||
*/ | ||
export declare const updater: (path: Path, fn: UpdateFn<any>) => FnO<any, any>; | ||
export declare const defUpdaterUnsafe: <T = any>(path: Path, fn: FnO<T, T>) => FnO<T, any>; | ||
/** | ||
* Type checked version of {@link updater}. | ||
* Type checked version of {@link defUpdaterUnsafe}. Only the first 8 | ||
* path levels are type checked. | ||
* | ||
@@ -31,12 +38,12 @@ * @param path - | ||
*/ | ||
export declare function updaterT<T>(path: [], fn: UpdateFn<T>): FnO<T, T>; | ||
export declare function updaterT<T, A extends Keys<T>>(path: [A], fn: UpdateFn<Val1<T, A>>): FnO<T, T>; | ||
export declare function updaterT<T, A extends Keys<T>, B extends Keys1<T, A>>(path: [A, B], fn: UpdateFn<Val2<T, A, B>>): FnO<T, T>; | ||
export declare function updaterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>>(path: [A, B, C], fn: UpdateFn<Val3<T, A, B, C>>): FnO<T, T>; | ||
export declare function updaterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>>(path: [A, B, C, D], fn: UpdateFn<Val4<T, A, B, C, D>>): FnO<T, T>; | ||
export declare function updaterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>>(path: [A, B, C, D, E], fn: UpdateFn<Val5<T, A, B, C, D, E>>): FnO<T, T>; | ||
export declare function updaterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>>(path: [A, B, C, D, E, F], fn: UpdateFn<Val6<T, A, B, C, D, E, F>>): FnO<T, T>; | ||
export declare function updaterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>>(path: [A, B, C, D, E, F, G], fn: UpdateFn<Val7<T, A, B, C, D, E, F, G>>): FnO<T, T>; | ||
export declare function updaterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(path: [A, B, C, D, E, F, G, H], fn: UpdateFn<Val8<T, A, B, C, D, E, F, G, H>>): FnO<T, T>; | ||
export declare function updaterT<T, A extends Keys<T>, B extends Keys1<T, A>, C extends Keys2<T, A, B>, D extends Keys3<T, A, B, C>, E extends Keys4<T, A, B, C, D>, F extends Keys5<T, A, B, C, D, E>, G extends Keys6<T, A, B, C, D, E, F>, H extends Keys7<T, A, B, C, D, E, F, G>>(path: [A, B, C, D, E, F, G, H, ...PropertyKey[]], fn: UpdateFn<any>): FnO<T, T>; | ||
export declare function defUpdater<T>(path: Path0, fn: UpdateFn<T, T>): FnO<T, T>; | ||
export declare function defUpdater<T, A>(path: Path1<T, A>, fn: UpdateFn<OptPathVal<T, [A]>, PathVal<T, [A]>>): FnO<T, T>; | ||
export declare function defUpdater<T, A, B>(path: Path2<T, A, B>, fn: UpdateFn<OptPathVal<T, [A, B]>, PathVal<T, [A, B]>>): FnO<T, T>; | ||
export declare function defUpdater<T, A, B, C>(path: Path3<T, A, B, C>, fn: UpdateFn<OptPathVal<T, [A, B, C]>, PathVal<T, [A, B, C]>>): FnO<T, T>; | ||
export declare function defUpdater<T, A, B, C, D>(path: Path4<T, A, B, C, D>, fn: UpdateFn<OptPathVal<T, [A, B, C, D]>, PathVal<T, [A, B, C, D]>>): FnO<T, T>; | ||
export declare function defUpdater<T, A, B, C, D, E>(path: Path5<T, A, B, C, D, E>, fn: UpdateFn<OptPathVal<T, [A, B, C, D, E]>, PathVal<T, [A, B, C, D, E]>>): FnO<T, T>; | ||
export declare function defUpdater<T, A, B, C, D, E, F>(path: Path6<T, A, B, C, D, E, F>, fn: UpdateFn<OptPathVal<T, [A, B, C, D, E, F]>, PathVal<T, [A, B, C, D, E, F]>>): FnO<T, T>; | ||
export declare function defUpdater<T, A, B, C, D, E, F, G>(path: Path7<T, A, B, C, D, E, F, G>, fn: UpdateFn<OptPathVal<T, [A, B, C, D, E, F, G]>, PathVal<T, [A, B, C, D, E, F, G]>>): FnO<T, T>; | ||
export declare function defUpdater<T, A, B, C, D, E, F, G, H>(path: Path8<T, A, B, C, D, E, F, G, H>, fn: UpdateFn<OptPathVal<T, [A, B, C, D, E, F, G, H]>, PathVal<T, [A, B, C, D, E, F, G, H]>>): FnO<T, T>; | ||
export declare function defUpdater<T, A, B, C, D, E, F, G, H>(path: DeepPath<T, A, B, C, D, E, F, G, H>, fn: UpdateFn<any, any>): FnO<T, T>; | ||
//# sourceMappingURL=updater.d.ts.map |
@@ -1,17 +0,23 @@ | ||
import { getterT } from "./getter"; | ||
import { setterT } from "./setter"; | ||
import { defGetter } from "./getter"; | ||
import { defSetter } from "./setter"; | ||
/** | ||
* Similar to {@link setter}, returns a function to update values at | ||
* given `path` using provided update `fn`. | ||
* Similar to {@link defSetterUnsafe}, returns a function to update | ||
* values at given `path` using provided update `fn`. Paths and the | ||
* arguments given to the returned function are NOT type checked. | ||
* | ||
* @remarks | ||
* The returned function accepts a single object / array and applies | ||
* `fn` to current path value (incl. any additional/optional arguments | ||
* passed) and uses result as new value. Does not modify original state | ||
* (unless given function does so itself). | ||
* `fn` to given path value (incl. any additional / optional arguments | ||
* passed) and uses result as new value. Does not modify original state. | ||
* | ||
* ``` | ||
* add = updater("a.b", (x, n) => x + n); | ||
* The type parameter `T` can be used to indicate the type of the nested | ||
* value to be updated (default: `any`). | ||
* | ||
* add({a: {b: 10}}, 13); | ||
* @example | ||
* ```ts | ||
* const incB = defUpdaterUnsafe("a.b", (x, n) => x + n); | ||
* // or | ||
* const incB = defUpdaterUnsafe(["a", "b"], (x, n) => x + n); | ||
* | ||
* incB({ a: { b: 10 } }, 13); | ||
* // { a: { b: 23 } } | ||
@@ -23,7 +29,7 @@ * ``` | ||
*/ | ||
export const updater = (path, fn) => updaterT(path, fn); | ||
export function updaterT(path, fn) { | ||
const g = getterT(path); | ||
const s = setterT(path); | ||
export const defUpdaterUnsafe = (path, fn) => defUpdater(path, fn); | ||
export function defUpdater(path, fn) { | ||
const g = defGetter(path); | ||
const s = defSetter(path); | ||
return (state, ...args) => s(state, fn.apply(null, (args.unshift(g(state)), args))); | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1358
335
93537
3
35
+ Addedtslib@^1.11.1
+ Addedtslib@1.14.1(transitive)
Updated@thi.ng/checks@^2.6.0
Updated@thi.ng/errors@^1.2.8