Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4585b0d

Browse filesBrowse files
authored
Improve flow types for functional merge() definitions. (immutable-js#1400)
These previously did not include $Shape and dubiously allowed incorrect Iterables. Added some helper types to make defining the merge() family more robust, and lift some of those improvements into `record.merge()` as well. Fixes immutable-js#1396
1 parent 2ca04cb commit 4585b0d
Copy full SHA for 4585b0d

File tree

2 files changed

+181
-12
lines changed
Filter options

2 files changed

+181
-12
lines changed

‎type-definitions/immutable.js.flow

Copy file name to clipboardExpand all lines: type-definitions/immutable.js.flow
+22-12Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ type $ValOf<C, K = $KeyOf<C>> = $Call<
4949
K
5050
>;
5151

52+
type $IterableOf<C> = $Call<
53+
& (<V: Array<any> | IndexedCollection<any> | SetCollection<any>>(V) => Iterable<$ValOf<V>>)
54+
& (<V: KeyedCollection<any, any> | RecordInstance<any> | PlainObjInput<any, any>>(V) => Iterable<[$KeyOf<V>, $ValOf<V>]>),
55+
C
56+
>;
57+
5258
declare class _Collection<K, +V> /*implements ValueObject*/ {
5359
equals(other: mixed): boolean;
5460
hashCode(): number;
@@ -1372,6 +1378,10 @@ type RecordFactory<Values: Object> = Class<RecordInstance<Values>>;
13721378
// The type of runtime Record instances.
13731379
type RecordOf<Values: Object> = RecordInstance<Values> & Values;
13741380

1381+
// The values of a Record instance.
1382+
type _RecordValues<T, R: RecordInstance<T> | T> = R;
1383+
type RecordValues<R> = _RecordValues<*, R>;
1384+
13751385
declare function isRecord(maybeRecord: any): boolean %checks(maybeRecord instanceof RecordInstance);
13761386
declare class Record {
13771387
static <Values: Object>(spec: Values, name?: string): RecordFactory<Values>;
@@ -1383,10 +1393,10 @@ declare class Record {
13831393
}
13841394

13851395
declare class RecordInstance<T: Object> {
1386-
static (values?: $Shape<T> | Iterable<[$Keys<T>, any]>): RecordOf<T>;
1396+
static (values?: Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>): RecordOf<T>;
13871397
// Note: a constructor can only create an instance of RecordInstance<T>,
13881398
// it's encouraged to not use `new` when creating Records.
1389-
constructor (values?: $Shape<T> | Iterable<[$Keys<T>, any]>): void;
1399+
constructor (values?: Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>): void;
13901400

13911401
size: number;
13921402

@@ -1407,16 +1417,16 @@ declare class RecordInstance<T: Object> {
14071417

14081418
set<K: $Keys<T>>(key: K, value: $ElementType<T, K>): this & T;
14091419
update<K: $Keys<T>>(key: K, updater: (value: $ElementType<T, K>) => $ElementType<T, K>): this & T;
1410-
merge(...collections: Array<$Shape<T> | Iterable<[$Keys<T>, any]>>): this & T;
1411-
mergeDeep(...collections: Array<$Shape<T> | Iterable<[$Keys<T>, any]>>): this & T;
1420+
merge(...collections: Array<Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>>): this & T;
1421+
mergeDeep(...collections: Array<Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>>): this & T;
14121422

14131423
mergeWith(
1414-
merger: (oldVal: any, newVal: any, key: $Keys<T>) => any,
1415-
...collections: Array<$Shape<T> | Iterable<[$Keys<T>, any]>>
1424+
merger: (oldVal: $ValOf<T>, newVal: $ValOf<T>, key: $Keys<T>) => $ValOf<T>,
1425+
...collections: Array<Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>>
14161426
): this & T;
14171427
mergeDeepWith(
14181428
merger: (oldVal: any, newVal: any, key: any) => any,
1419-
...collections: Array<$Shape<T> | Iterable<[$Keys<T>, any]>>
1429+
...collections: Array<Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>>
14201430
): this & T;
14211431

14221432
delete<K: $Keys<T>>(key: K): this & T;
@@ -1471,7 +1481,7 @@ declare class RecordInstance<T: Object> {
14711481
wasAltered(): boolean;
14721482
asImmutable(): this & T;
14731483

1474-
@@iterator(): Iterator<[$Keys<T>, any]>;
1484+
@@iterator(): Iterator<[$Keys<T>, $ValOf<T>]>;
14751485
}
14761486

14771487
declare function fromJS(
@@ -1533,21 +1543,21 @@ declare function updateIn<C, K: $KeyOf<C>, K2: $KeyOf<$ValOf<C, K>>, K3: $KeyOf<
15331543

15341544
declare function merge<C>(
15351545
collection: C,
1536-
...collections: Array<Iterable<$ValOf<C>> | Iterable<[$KeyOf<C>, $ValOf<C>]> | PlainObjInput<$KeyOf<C>, $ValOf<C>>>
1546+
...collections: Array<$IterableOf<C> | $Shape<RecordValues<C>> | PlainObjInput<$KeyOf<C>, $ValOf<C>>>
15371547
): C;
15381548
declare function mergeWith<C>(
15391549
merger: (oldVal: $ValOf<C>, newVal: $ValOf<C>, key: $KeyOf<C>) => $ValOf<C>,
15401550
collection: C,
1541-
...collections: Array<Iterable<$ValOf<C>> | Iterable<[$KeyOf<C>, $ValOf<C>]> | PlainObjInput<$KeyOf<C>, $ValOf<C>>>
1551+
...collections: Array<$IterableOf<C> | $Shape<RecordValues<C>> | PlainObjInput<$KeyOf<C>, $ValOf<C>>>
15421552
): C;
15431553
declare function mergeDeep<C>(
15441554
collection: C,
1545-
...collections: Array<Iterable<$ValOf<C>> | Iterable<[$KeyOf<C>, $ValOf<C>]> | PlainObjInput<$KeyOf<C>, $ValOf<C>>>
1555+
...collections: Array<$IterableOf<C> | $Shape<RecordValues<C>> | PlainObjInput<$KeyOf<C>, $ValOf<C>>>
15461556
): C;
15471557
declare function mergeDeepWith<C>(
15481558
merger: (oldVal: any, newVal: any, key: any) => mixed,
15491559
collection: C,
1550-
...collections: Array<Iterable<$ValOf<C>> | Iterable<[$KeyOf<C>, $ValOf<C>]> | PlainObjInput<$KeyOf<C>, $ValOf<C>>>
1560+
...collections: Array<$IterableOf<C> | $Shape<RecordValues<C>> | PlainObjInput<$KeyOf<C>, $ValOf<C>>>
15511561
): C;
15521562

15531563
export {

‎type-definitions/tests/merge.js

Copy file name to clipboard
+159Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import {
11+
List,
12+
Map,
13+
Record,
14+
type RecordOf,
15+
type RecordFactory,
16+
get,
17+
getIn,
18+
has,
19+
hasIn,
20+
merge,
21+
mergeDeep,
22+
mergeWith,
23+
mergeDeepWith,
24+
remove,
25+
removeIn,
26+
set,
27+
setIn,
28+
update,
29+
updateIn,
30+
} from '../../';
31+
32+
// merge: Records
33+
34+
type XYPoint = { x: number, y: number };
35+
type XYPointRecord = RecordOf<XYPoint>;
36+
const xyRecord: RecordFactory<XYPoint> = Record({ x: 0, y: 0 });
37+
const record = xyRecord();
38+
(merge(record, { x: 321 }): XYPointRecord);
39+
(merge(record, xyRecord({ x: 321 })): XYPointRecord);
40+
// $ExpectError
41+
(merge(record, { z: 321 }): XYPointRecord);
42+
// $ExpectError
43+
(merge(record, { x: 'abc' }): XYPointRecord);
44+
(merge(record, [['x', 321]]): XYPointRecord);
45+
// $ExpectError
46+
(merge(record, [['z', 321]]): XYPointRecord);
47+
// $ExpectError
48+
(merge(record, [['x', 'abc']]): XYPointRecord);
49+
// $ExpectError
50+
(merge(record, [321]): XYPointRecord);
51+
(merge(record, Map({x: 123})): XYPointRecord);
52+
// $ExpectError
53+
(merge(record, Map({z: 123})): XYPointRecord);
54+
(merge(record, Map([['x', 123]])): XYPointRecord);
55+
// $ExpectError
56+
(merge(record, Map([['z', 123]])): XYPointRecord);
57+
// $ExpectError
58+
(merge(record, List([123])): XYPointRecord);
59+
60+
// merge: Maps
61+
62+
const map = Map({ key: 'value' });
63+
(merge(map, { key: 'alternate' }): Map<string, string>);
64+
(merge(map, { otherKey: 'value' }): Map<string, string>);
65+
(merge(map, Map({ key: 'alternate' })): Map<string, string>);
66+
(merge(map, Map({ otherKey: 'value' })): Map<string, string>);
67+
(merge(map, [['otherKey', 'value']]): Map<string, string>);
68+
// $ExpectError (functional merge cannot return union value types)
69+
(merge(map, Map({ otherKey: 123 })): Map<string, string|number>);
70+
// $ExpectError
71+
(merge(map, [ 4, 5, 6 ]): Map<string, string>);
72+
// $ExpectError
73+
(merge(map, 123): Map<string, string>);
74+
// $ExpectError
75+
(merge(map, { 0: 123 }): Map<string, string>);
76+
// $ExpectError
77+
(merge(map, [[0, 4], [1, 5], [1, 6]]): Map<string, string>);
78+
79+
// merge: Lists
80+
81+
const list = List([ 1, 2, 3 ]);
82+
(merge(list, [ 4, 5, 6 ]): List<number>);
83+
(merge(list, List([ 4, 5, 6 ])): List<number>);
84+
// $ExpectError (functional merge cannot return union value types)
85+
(merge(list, [ 'a', 'b', 'c' ]): List<number|string>);
86+
// $ExpectError (functional merge cannot return union value types)
87+
(merge(list, List([ 'a', 'b', 'c' ])): List<number|string>);
88+
// $ExpectError
89+
(merge(list, 123): List<number>);
90+
// $ExpectError
91+
(merge(list, { 0: 123 }): List<number>);
92+
// $ExpectError
93+
(merge(list, Map({ 0: 123 })): List<number>);
94+
// $ExpectError
95+
(merge(list, [[0, 4], [1, 5], [1, 6]]): List<number>);
96+
97+
// merge: Objects as Records
98+
99+
const objRecord: XYPoint = { x: 12, y: 34 };
100+
(merge(objRecord, { x: 321 }): XYPoint);
101+
(merge(objRecord, xyRecord({ x: 321 })): XYPoint);
102+
// $ExpectError
103+
(merge(objRecord, { z: 321 }): XYPoint);
104+
// $ExpectError
105+
(merge(objRecord, { x: 'abc' }): XYPoint);
106+
(merge(objRecord, [['x', 321]]): XYPoint);
107+
// $ExpectError
108+
(merge(objRecord, [['z', 321]]): XYPoint);
109+
// $ExpectError
110+
(merge(objRecord, [['x', 'abc']]): XYPoint);
111+
// $ExpectError
112+
(merge(objRecord, [321]): XYPoint);
113+
(merge(objRecord, Map({x: 123})): XYPoint);
114+
// $ExpectError
115+
(merge(objRecord, Map({z: 123})): XYPoint);
116+
(merge(objRecord, Map([['x', 123]])): XYPoint);
117+
// $ExpectError
118+
(merge(objRecord, Map([['z', 123]])): XYPoint);
119+
// $ExpectError
120+
(merge(objRecord, List([123])): XYPoint);
121+
122+
// merge: Objects as Maps
123+
124+
type ObjMap<T> = { [key: string]: T };
125+
const objMap: ObjMap<number> = { x: 12, y: 34 };
126+
(merge(objMap, { x: 321 }): ObjMap<number>);
127+
(merge(objMap, { z: 321 }): ObjMap<number>);
128+
// $ExpectError
129+
(merge(objMap, { x: 'abc' }): ObjMap<number>);
130+
(merge(objMap, [['x', 321]]): ObjMap<number>);
131+
(merge(objMap, [['z', 321]]): ObjMap<number>);
132+
// $ExpectError
133+
(merge(objMap, [['x', 'abc']]): ObjMap<number>);
134+
// $ExpectError
135+
(merge(objMap, [321]): ObjMap<number>);
136+
(merge(objMap, Map({x: 123})): ObjMap<number>);
137+
(merge(objMap, Map({z: 123})): ObjMap<number>);
138+
(merge(objMap, Map([['x', 123]])): ObjMap<number>);
139+
(merge(objMap, Map([['z', 123]])): ObjMap<number>);
140+
// $ExpectError
141+
(merge(objMap, List([123])): ObjMap<number>);
142+
143+
// merge: Arrays
144+
145+
const arr = [ 1, 2, 3 ];
146+
(merge(arr, [ 4, 5, 6 ]): Array<number>);
147+
(merge(arr, List([ 4, 5, 6 ])): Array<number>);
148+
// $ExpectError (functional merge cannot return union value types)
149+
(merge(arr, [ 'a', 'b', 'c' ]): Array<number|string>);
150+
// $ExpectError (functional merge cannot return union value types)
151+
(merge(arr, List([ 'a', 'b', 'c' ])): Array<number|string>);
152+
// $ExpectError
153+
(merge(arr, 123): Array<number>);
154+
// $ExpectError
155+
(merge(arr, { 0: 123 }): Array<number>);
156+
// $ExpectError
157+
(merge(arr, Map({ 0: 123 })): Array<number>);
158+
// $ExpectError
159+
(merge(arr, [[0, 4], [1, 5], [1, 6]]): Array<number>);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.