8000 Update functional types (#2068) · immutable-js/immutable-js@81673ba · GitHub
[go: up one dir, main page]

Skip to content

Commit 81673ba

Browse files
authored
Update functional types (#2068)
* Update functional types * type of "updateIn" * do not guess on hasIn
1 parent 1574697 commit 81673ba

File tree

3 files changed

+138
-26
lines changed

3 files changed

+138
-26
lines changed

__tests__/updateIn.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ describe('updateIn', () => {
3232
it('deep edit in raw JS', () => {
3333
const m = { a: { b: { c: [10] } } };
3434
expect(
35-
// @ts-expect-error -- `updateIn` should copy the comportment of `getIn`
3635
updateIn(m, ['a', 'b', 'c', 0], (value: number) => value * 2)
3736
).toEqual({
3837
a: { b: { c: [20] } },
@@ -197,7 +196,6 @@ describe('updateIn', () => {
197196
m,
198197
['a', 'b', 'z'],
199198
Map<string, number>(),
200-
// @ts-expect-error -- updateIn should handle the `notSetValue` parameter
201199
(map: Map<string, number>) => map.set('d', 20)
202200
)
203201
).toEqual({ a: { b: { c: 10, z: Map({ d: 20 }) } } });
@@ -223,7 +221,6 @@ describe('updateIn', () => {
223221

224222
it('update with notSetValue when non-existing key in raw JS', () => {
225223
const m = { a: { b: { c: 10 } } };
226-
// @ts-expect-error -- updateIn should handle the `notSetValue` parameter
227224
expect(updateIn(m, ['x'], 100, (map: number) => map + 1)).toEqual({
228225
a: { b: { c: 10 } },
229226
x: 101,

type-definitions/immutable.d.ts

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ declare namespace Immutable {
127127
: T extends Collection.Keyed<infer KeyedKey, infer V>
128128
? // convert KeyedCollection to DeepCopy plain JS object
129129
{
130-
[key in KeyedKey extends string | number | symbol
130+
[key in KeyedKey extends PropertyKey
131131
? KeyedKey
132132
: string]: V extends object ? unknown : V;
133133
}
@@ -853,9 +853,7 @@ declare namespace Immutable {
853853
* not altered.
854854
*/
855855
function Map<K, V>(collection?: Iterable<[K, V]>): Map<K, V>;
856-
function Map<R extends { [key in string | number | symbol]: unknown }>(
857-
obj: R
858-
): MapOf<R>;
856+
function Map<R extends { [key in PropertyKey]: unknown }>(obj: R): MapOf<R>;
859857
function Map<V>(obj: { [key: string]: V }): Map<string, V>;
860858
function Map<K extends string | symbol, V>(obj: { [P in K]?: V }): Map<K, V>;
861859

@@ -864,7 +862,7 @@ declare namespace Immutable {
864862
*
865863
* @ignore
866864
*/
867-
interface MapOf<R extends { [key in string | number | symbol]: unknown }>
865+
interface MapOf<R extends { [key in PropertyKey]: unknown }>
868866
extends Map<keyof R, R[keyof R]> {
869867
/**
870868
* Returns the value associated with the provided key, or notSetValue if
@@ -3155,14 +3153,14 @@ declare namespace Immutable {
31553153
*
31563154
* Converts keys to Strings.
31573155
*/
3158-
toJS(): { [key in string | number | symbol]: DeepCopy<V> };
3156+
toJS(): { [key in PropertyKey]: DeepCopy<V> };
31593157

31603158
/**
31613159
* Shallowly converts this Keyed Seq to equivalent native JavaScript Object.
31623160
*
31633161
* Converts keys to Strings.
31643162
*/
3165-
toJSON(): { [key in string | number | symbol]: V };
3163+
toJSON(): { [key in PropertyKey]: V };
31663164

31673165
/**
31683166
* Shallowly converts this collection to an Array.
@@ -3763,14 +3761,14 @@ declare namespace Immutable {
37633761
*
37643762
* Converts keys to Strings.
37653763
*/
3766-
toJS(): { [key in string | number | symbol]: DeepCopy<V> };
3764+
toJS(): { [key in PropertyKey]: DeepCopy<V> };
37673765

37683766
/**
37693767
* Shallowly converts this Keyed collection to equivalent native JavaScript Object.
37703768
*
37713769
* Converts keys to Strings.
37723770
*/
3773-
toJSON(): { [key in string | number | symbol]: V };
3771+
toJSON(): { [key in PropertyKey]: V };
37743772

37753773
/**
37763774
* Shallowly converts this collection to an Array.
@@ -4520,17 +4518,15 @@ declare namespace Immutable {
45204518
* `Collection.Indexed`, and `Collection.Set` become `Array`, while
45214519
* `Collection.Keyed` become `Object`, converting keys to Strings.
45224520
*/
4523-
toJS():
4524-
| Array<DeepCopy<V>>
4525-
| { [key in string | number | symbol]: DeepCopy<V> };
4521+
toJS(): Array<DeepCopy<V>> | { [key in PropertyKey]: DeepCopy<V> };
45264522

45274523
/**
45284524
* Shallowly converts this Collection to equivalent native JavaScript Array or Object.
45294525
*
45304526
* `Collection.Indexed`, and `Collection.Set` become `Array`, while
45314527
* `Collection.Keyed` become `Object`, converting keys to Strings.
45324528
*/
4533-
toJSON(): Array<V> | { [key in string | number | symbol]: V };
4529+
toJSON(): Array<V> | { [key in PropertyKey]: V };
45344530

45354531
/**
45364532
* Shallowly converts this collection to an Array.
@@ -5749,9 +5745,12 @@ declare namespace Immutable {
57495745
key: K,
57505746
notSetValue: unknown
57515747
): C[K];
5752-
function get<V>(collection: { [key: string]: V }, key: string): V | undefined;
5748+
function get<V>(
5749+
collection: { [key: PropertyKey]: V },
5750+
key: string
5751+
): V | undefined;
57535752
function get<V, NSV>(
5754-
collection: { [key: string]: V },
5753+
collection: { [key: PropertyKey]: V },
57555754
key: string,
57565755
notSetValue: NSV
57575756
): V | NSV;
@@ -5971,7 +5970,11 @@ declare namespace Immutable {
59715970
* hasIn({ x: { y: { z: 123 }}}, ['x', 'q', 'p']) // false
59725971
* ```
59735972
*/
5974-
function hasIn(collection: unknown, keyPath: Iterable<unknown>): boolean;
5973+
function hasIn(
5974+
collection: string | boolean | number,
5975+
keyPath: KeyPath<unknown>
5976+
): never;
5977+
function hasIn<K>(collection: unknown, keyPath: KeyPath<K>): boolean;
59755978

59765979
/**
59775980
* Returns a copy of the collection with the value at the key path removed.
@@ -6025,17 +6028,83 @@ declare namespace Immutable {
60256028
* console.log(original) // { x: { y: { z: 123 }}}
60266029
* ```
60276030
*/
6028-
function updateIn<C>(
6031+
function updateIn<K extends PropertyKey, V, C extends Collection<K, V>>(
60296032
collection: C,
6030-
keyPath: Iterable<unknown>,
6031-
updater: (value: unknown) => unknown
6033+
keyPath: KeyPath<K>,
6034+
updater: (
6035+
value: RetrievePath<C, Array<K>> | undefined
6036+
) => unknown | undefined
60326037
): C;
6033-
function updateIn<C>(
6038+
function updateIn<K extends PropertyKey, V, C extends Collection<K, V>, NSV>(
60346039
collection: C,
6035-
keyPath: Iterable<unknown>,
6036-
notSetValue: unknown,
6037-
updater: (value: unknown) => unknown
6040+
keyPath: KeyPath<K>,
6041+
notSetValue: NSV,
6042+
updater: (value: RetrievePath<C, Array<K>> | NSV) => unknown
6043+
): C;
6044+
function updateIn<
6045+
TProps extends object,
6046+
C extends Record<TProps>,
6047+
K extends keyof TProps,
6048+
>(
6049+
record: C,
6050+
keyPath: KeyPath<K>,
6051+
updater: (value: RetrievePath<C, Array<K>>) => unknown
6052+
): C;
6053+
function updateIn<
6054+
TProps extends object,
6055+
C extends Record<TProps>,
6056+
K extends keyof TProps,
6057+
NSV,
6058+
>(
6059+
record: C,
6060+
keyPath: KeyPath<K>,
6061+
notSetValue: NSV,
6062+
updater: (value: RetrievePath<C, Array<K>> | NSV) => unknown
60386063
): C;
6064+
function updateIn<K extends PropertyKey, V, C extends Array<V>>(
6065+
collection: Array<V>,
6066+
keyPath: KeyPath<string | number>,
6067+
updater: (
6068+
value: RetrievePath<C, Array<K>> | undefined
6069+
) => unknown | undefined
6070+
): Array<V>;
6071+
function updateIn<K extends PropertyKey, V, C extends Array<V>, NSV>(
6072+
collection: Array<V>,
6073+
keyPath: KeyPath<K>,
6074+
notSetValue: NSV,
6075+
updater: (value: RetrievePath<C, Array<K>> | NSV) => unknown
6076+
): Array<V>;
6077+
function updateIn<K extends PropertyKey, C>(
6078+
object: C,
6079+
keyPath: KeyPath<K>,
6080+
updater: (value: RetrievePath<C, Array<K>>) => unknown
6081+
): C;
6082+
function updateIn<K extends PropertyKey, C, NSV>(
6083+
object: C,
6084+
keyPath: KeyPath<K>,
6085+
notSetValue: NSV,
6086+
updater: (value: RetrievePath<C, Array<K>> | NSV) => unknown
6087+
): C;
6088+
function updateIn<
6089+
K extends PropertyKey,
6090+
V,
6091+
C extends { [key: PropertyKey]: V },
6092+
>(
6093+
collection: C,
6094+
keyPath: KeyPath<K>,
6095+
updater: (value: RetrievePath<C, Array<K>>) => unknown
6096+
): { [key: PropertyKey]: V };
6097+
function updateIn<
6098+
K extends PropertyKey,
6099+
V,
6100+
C extends { [key: PropertyKey]: V },
6101+
NSV,
6102+
>(
6103+
collection: C,
6104+
keyPath: KeyPath<K>,
6105+
notSetValue: NSV,
6106+
updater: (value: RetrievePath<C, Array<K>> | NSV) => unknown
6107+
): { [key: PropertyKey]: V };
60396108

60406109
/**
60416110
* Returns a copy of the collection with the remaining collections merged in.

type-definitions/ts-tests/functional.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
get,
44
getIn,
55
has,
6+
hasIn,
67
set,
78
remove,
89
update,
@@ -84,6 +85,51 @@ test('has', () => {
8485
expect(has({ x: 10, y: 20 }, 'x')).type.toBeBoolean();
8586
});
8687

88+
test('hasIn', () => {
89+
expect(hasIn('a', ['length' as const])).type.toBe<never>();
90+
91+
expect(hasIn(123, [])).type.toBe<never>();
92+
93+
expect(hasIn(true, [])).type.toBe<never>();
94+
95+
expect(hasIn([1, 2, 3], [0])).type.toBe<boolean>();
96+
97+
// first parameter type is Array<number> so we can not detect that the number will be invalid
98+
expect(hasIn([1, 2, 3], [99])).type.toBe<boolean>();
99+
100+
// We do not handle List in hasIn TS type yet (hard to convert to a tuple)
101+
expect(hasIn([1, 2, 3], List([0]))).type.toBe<boolean>();
102+
103+
expect(hasIn(List([1, 2, 3]), [0])).type.toBe<boolean>();
104+
105+
// first parameter type is Array<number> so we can not detect that the number will be invalid
106+
expect(hasIn(List([1, 2, 3]), [99])).type.toBe<boolean>();
107+
108+
expect(hasIn(List([1, 2, 3]), ['a' as const])).type.toBe<boolean>();
109+
110+
expect(hasIn({ x: 10, y: 20 }, ['x' as const])).type.toBe<boolean>();
111+
112+
CE87 expect(hasIn({ x: { y: 20 } }, ['z' as const])).type.toBe<boolean>();
113+
114+
expect(
115+
hasIn({ x: { y: 20 } }, ['x' as const, 'y' as const])
116+
).type.toBe<boolean>();
117+
118+
expect(
119+
hasIn({ x: Map({ y: 20 }) }, ['x' as const, 'y' as const])
120+
).type.toBe<boolean>();
121+
122+
expect(
123+
hasIn(Map({ x: Map({ y: 20 }) }), ['x' as const, 'y' as const])
124+
).type.toBe<boolean>();
125+
126+
const o = Map({ x: List([Map({ y: 20 })]) });
127+
128+
expect(hasIn(o, ['x' as const, 'y' as const])).type.toBe<boolean>();
129+
130+
expect(hasIn(o, ['x' as const, 0, 'y' as const])).type.toBe<boolean>();
131+
});
132+
87133
test('set', () => {
88134
expect(set([1, 2, 3], 0, 10)).type.toBe<number[]>();
89135

0 commit comments

Comments
 (0)
0