-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BREAKING CHANGE: get and set functions were moved into lens module
- Loading branch information
Showing
10 changed files
with
257 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as set } from './set' | ||
export { default as get } from './get' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/** | ||
* @module Lens | ||
*/ | ||
|
||
import type { Unshift } from '../types/unshift' | ||
import permutation3 from '../permutation/permutation-3' | ||
import type { Flatten, Flattenable } from '../types/flatten' | ||
|
||
/** | ||
* Sets the `Value` type inside a nested object type `Source` by provided `Path` | ||
* written in dot-notation. | ||
*/ | ||
export type Set< | ||
Source extends Flattenable, | ||
Path extends string, | ||
Value | ||
> = | ||
Source extends Record<string, unknown> | ||
? Path extends `${number}.${infer Right}` | ||
? Set<Source, Right, Value> | ||
// @ts-ignore | ||
: Path extends `${infer Left extends keyof Source}.${infer Right}` | ||
? { | ||
[Key in keyof Source]: Key extends Exclude<keyof Source, Left> | ||
? Source[Key] | ||
// @ts-ignore | ||
: Set<Exclude<Source[Left], undefined>, Right, Value> | ||
} | ||
: Path extends keyof Source | ||
? { | ||
[Key in keyof Source]: Key extends Exclude<keyof Source, Path> | ||
? Source[Key] | ||
: Value | ||
} | ||
: never | ||
: Source extends any[] | ||
? Path extends `${infer Index extends number}.${infer Right}` | ||
? Unshift<Source, Index, Set<Exclude<Source[Index], undefined>, Right, Value>> | ||
: Path extends `${infer Index extends number}` | ||
? Unshift<Source, Index, Value> | ||
: never | ||
: never | ||
|
||
/** | ||
* Sets the `value` inside a nested `source` object by provided `path` | ||
* written in dot-notation. | ||
*/ | ||
// @ts-ignore | ||
export default function set< | ||
Source extends Flattenable, | ||
Path extends Flatten<Source>, | ||
Value | ||
> ( | ||
path: Path, | ||
value: Value, | ||
// @ts-ignore | ||
): (source: Source) => Set<Source, Path, Value> | ||
|
||
/** | ||
* Sets the `value` inside a nested `source` object by provided `path` | ||
* written in dot-notation. | ||
*/ | ||
// @ts-ignore | ||
export default function set< | ||
Source extends Flattenable, | ||
Path extends Flatten<Source>, | ||
Value | ||
> ( | ||
source: Source, | ||
path: Path, | ||
value: Value | ||
// @ts-ignore | ||
): Set<Source, Path, Value> | ||
|
||
/** | ||
* Sets the `value` inside a nested `source` object by provided `path` | ||
* written in dot-notation. | ||
*/ | ||
export default function set (...args: [any, any, any?]): any { | ||
// @ts-ignore | ||
return permutation3( | ||
// @ts-ignore | ||
< | ||
Source extends Flattenable, | ||
Path extends Flatten<Source>, | ||
Value | ||
>( | ||
source: Source, | ||
path: Path, | ||
value: Value | ||
// @ts-ignore | ||
): Set<Source, Path, Value> => { | ||
// @ts-ignore | ||
const keys = path.split('.') | ||
const length = keys.length | ||
|
||
let result = structuredClone(source) | ||
|
||
if (length === 0) { | ||
result[path as keyof Source] = value as Source[keyof Source] | ||
} | ||
|
||
let scope: any = result[keys[0]! as keyof Source] | ||
|
||
for (let index = 1; index < length; index += 1) { | ||
if (index === length - 1) { | ||
scope[keys?.[index] as keyof typeof scope] = value | ||
} else { | ||
scope = scope?.[keys?.[index] as keyof typeof scope] | ||
} | ||
} | ||
|
||
// @ts-ignore | ||
return result as Set<Source, Path, Value> | ||
} | ||
)(...args) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import sut from '../src/lens/set' | ||
|
||
describe('set', () => { | ||
describe('when setting a property', () => { | ||
it('should return source copy with modified value', () => { | ||
const source = { a: { b: { c: 1 } } } | ||
const result = sut(source, 'a.b.c', 2) | ||
|
||
expect(result).toEqual({ a: { b: { c: 2 } } }) | ||
expect(source === result).toBe(false) | ||
}) | ||
}) | ||
|
||
describe('when setting a nested array\'s element', () => { | ||
it('should return source copy with modified value', () => { | ||
const source = { a: { b: { c: [1] } } } | ||
const result = sut(source, 'a.b.c.0', 2) | ||
|
||
expect(result).toEqual({ a: { b: { c: [2] } } }) | ||
expect(source === result).toBe(false) | ||
}) | ||
|
||
describe('when an element is object', () => { | ||
it('should return source copy with modified value', () => { | ||
const source = { a: { b: { c: [{ a: { b: 2 } }] } } } | ||
const result = sut(source, 'a.b.c.0.a.b', 3) | ||
|
||
expect(result).toEqual({ a: { b: { c: [{ a: { b: 3 } }] } } }) | ||
expect(source === result).toBe(false) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import get from '../src/get' | ||
import get from '../src/lens/get' | ||
|
||
import tacit from './tacit' | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import set from '../src/lens/set' | ||
|
||
import tacit from './tacit' | ||
|
||
const source = { | ||
a: 1, | ||
b: { | ||
c: true, | ||
d: { | ||
e: [ | ||
{ | ||
f: '' | ||
} | ||
], | ||
g: [ | ||
3 | ||
] | ||
} | ||
} | ||
} | ||
|
||
// region full | ||
|
||
const a1: typeof source = set( | ||
source, | ||
'a', | ||
2 | ||
) | ||
|
||
const a2: typeof source = set( | ||
source, | ||
'b.c', | ||
false | ||
) | ||
|
||
const a3: typeof source = set( | ||
source, | ||
'b.d.e.0.f', | ||
'f' | ||
) | ||
|
||
const a4: typeof source = set( | ||
source, | ||
'b.d.g', | ||
[2] | ||
) | ||
|
||
// endregion | ||
|
||
// region tacit | ||
|
||
const b1: typeof source = tacit( | ||
set( | ||
'a', | ||
2 | ||
), | ||
source | ||
) | ||
|
||
const b2: typeof source = tacit( | ||
set( | ||
'b.c', | ||
false | ||
), | ||
source | ||
) | ||
|
||
const b3: typeof source = tacit( | ||
set( | ||
'b.d.e.0.f', | ||
'f' | ||
), | ||
source | ||
) | ||
|
||
const b4: typeof source = tacit( | ||
set( | ||
'b.d.g', | ||
[2] | ||
), | ||
source | ||
) | ||
|
||
// endregion |