Skip to content

Commit ffb3ee8

Browse files
authored
better type for Record.toJS (#1917)
1 parent cfd7957 commit ffb3ee8

File tree

7 files changed

+162
-13
lines changed

7 files changed

+162
-13
lines changed

type-definitions/immutable.d.ts

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,30 @@
9191
*/
9292

9393
declare namespace Immutable {
94+
/**
95+
* @ignore
96+
*/
97+
export type DeepCopy<T> = T extends Collection.Keyed<infer KeyedKey, infer V>
98+
? // convert KeyedCollection to DeepCopy plain JS object
99+
{
100+
[key in KeyedKey extends string | number | symbol
101+
? KeyedKey
102+
: string]: DeepCopy<V>;
103+
}
104+
: // convert IndexedCollection or Immutable.Set to DeepCopy plain JS array
105+
T extends Collection<infer _, infer V>
106+
? Array<DeepCopy<V>>
107+
: T extends string | number // Iterable scalar types : should be kept as is
108+
? T
109+
: T extends Iterable<infer V> // Iterable are converted to plain JS array
110+
? Array<DeepCopy<V>>
111+
: T extends object // plain JS object are converted deeply
112+
? {
113+
[ObjectKey in keyof T]: DeepCopy<T[ObjectKey]>;
114+
}
115+
: // other case : should be kept as is
116+
T;
117+
94118
/**
95119
* Lists are ordered indexed dense collections, much like a JavaScript
96120
* Array.
@@ -2666,7 +2690,7 @@ declare namespace Immutable {
26662690
* Note: This method may not be overridden. Objects with custom
26672691
* serialization to plain JS may override toJSON() instead.
26682692
*/
2669-
toJS(): { [K in keyof TProps]: unknown };
2693+
toJS(): DeepCopy<TProps>;
26702694

26712695
/**
26722696
* Shallowly converts this Record to equivalent native JavaScript Object.
@@ -2826,14 +2850,14 @@ declare namespace Immutable {
28262850
*
28272851
* Converts keys to Strings.
28282852
*/
2829-
toJS(): { [key: string]: unknown };
2853+
toJS(): { [key in string | number | symbol]: DeepCopy<V> };
28302854

28312855
/**
28322856
* Shallowly converts this Keyed Seq to equivalent native JavaScript Object.
28332857
*
28342858
* Converts keys to Strings.
28352859
*/
2836-
toJSON(): { [key: string]: V };
2860+
toJSON(): { [key in string | number | symbol]: V };
28372861

28382862
/**
28392863
* Shallowly converts this collection to an Array.
@@ -2968,7 +2992,7 @@ declare namespace Immutable {
29682992
/**
29692993
* Deeply converts this Indexed Seq to equivalent native JavaScript Array.
29702994
*/
2971-
toJS(): Array<unknown>;
2995+
toJS(): Array<DeepCopy<T>>;
29722996

29732997
/**
29742998
* Shallowly converts this Indexed Seq to equivalent native JavaScript Array.
@@ -3143,7 +3167,7 @@ declare namespace Immutable {
31433167
/**
31443168
* Deeply converts this Set Seq to equivalent native JavaScript Array.
31453169
*/
3146-
toJS(): Array<unknown>;
3170+
toJS(): Array<DeepCopy<T>>;
31473171

31483172
/**
31493173
* Shallowly converts this Set Seq to equivalent native JavaScript Array.
@@ -3453,14 +3477,14 @@ declare namespace Immutable {
34533477
*
34543478
* Converts keys to Strings.
34553479
*/
3456-
toJS(): { [key: string]: unknown };
3480+
toJS(): { [key in string | number | symbol]: DeepCopy<V> };
34573481

34583482
/**
34593483
* Shallowly converts this Keyed collection to equivalent native JavaScript Object.
34603484
*
34613485
* Converts keys to Strings.
34623486
*/
3463-
toJSON(): { [key: string]: V };
3487+
toJSON(): { [key in string | number | symbol]: V };
34643488

34653489
/**
34663490
* Shallowly converts this collection to an Array.
@@ -3635,7 +3659,7 @@ declare namespace Immutable {
36353659
/**
36363660
* Deeply converts this Indexed collection to equivalent native JavaScript Array.
36373661
*/
3638-
toJS(): Array<unknown>;
3662+
toJS(): Array<DeepCopy<T>>;
36393663

36403664
/**
36413665
* Shallowly converts this Indexed collection to equivalent native JavaScript Array.
@@ -3946,7 +3970,7 @@ declare namespace Immutable {
39463970
/**
39473971
* Deeply converts this Set collection to equivalent native JavaScript Array.
39483972
*/
3949-
toJS(): Array<unknown>;
3973+
toJS(): Array<DeepCopy<T>>;
39503974

39513975
/**
39523976
* Shallowly converts this Set collection to equivalent native JavaScript Array.
@@ -4208,15 +4232,17 @@ declare namespace Immutable {
42084232
* `Collection.Indexed`, and `Collection.Set` become `Array`, while
42094233
* `Collection.Keyed` become `Object`, converting keys to Strings.
42104234
*/
4211-
toJS(): Array<unknown> | { [key: string]: unknown };
4235+
toJS():
4236+
| Array<DeepCopy<V>>
4237+
| { [key in string | number | symbol]: DeepCopy<V> };
42124238

42134239
/**
42144240
* Shallowly converts this Collection to equivalent native JavaScript Array or Object.
42154241
*
42164242
* `Collection.Indexed`, and `Collection.Set` become `Array`, while
42174243
* `Collection.Keyed` become `Object`, converting keys to Strings.
42184244
*/
4219-
toJSON(): Array<V> | { [key: string]: V };
4245+
toJSON(): Array<V> | { [key in string | number | symbol]: V };
42204246

42214247
/**
42224248
* Shallowly converts this collection to an Array.

type-definitions/ts-tests/deepCopy.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { List, Map, Record, Set, Seq, DeepCopy, Collection } from 'immutable';
2+
3+
{
4+
// Basic types
5+
6+
// $ExpectType { a: number; b: number; }
7+
type Test = DeepCopy<{ a: number; b: number }>;
8+
// ^?
9+
10+
// $ExpectType number
11+
type TestA = Test['a'];
12+
// ^?
13+
}
14+
15+
{
16+
// Iterables
17+
18+
// $ExpectType string[]
19+
type Test = DeepCopy<string[]>;
20+
// ^?
21+
22+
// $ExpectType number[]
23+
type Keyed = DeepCopy<Collection.Indexed<number>>;
24+
// ^?
25+
}
26+
27+
{
28+
// Immutable first-level types
29+
30+
// $ExpectType { [x: string]: string; }
31+
type StringKey = DeepCopy<Map<string, string>>;
32+
33+
// $ExpectType { [x: string]: object; }
34+
type ObjectKey = DeepCopy<Map<object, object>>;
35+
36+
// $ExpectType { [x: string]: object; [x: number]: object; }
37+
type MixedKey = DeepCopy<Map<object | number, object>>;
38+
39+
// $ExpectType string[]
40+
type ListDeepCopy = DeepCopy<List<string>>;
41+
42+
// $ExpectType string[]
43+
type SetDeepCopy = DeepCopy<Set<string>>;
44+
}
45+
46+
{
47+
// Keyed
48+
49+
// $ExpectType { [x: string]: number; }
50+
type Keyed = DeepCopy<Collection.Keyed<string, number>>;
51+
52+
// $ExpectType { [x: string]: number; [x: number]: number; }
53+
type KeyedMixed = DeepCopy<Collection.Keyed<string | number, number>>;
54+
55+
// $ExpectType { [x: string]: number; [x: number]: number; }
56+
type KeyedSeqMixed = DeepCopy<Seq.Keyed<string | number, number>>;
57+
58+
// $ExpectType { [x: string]: number; [x: number]: number; }
59+
type MapMixed = DeepCopy<Map<string | number, number>>;
60+
}
61+
62+
{
63+
// Nested
64+
65+
// $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; }
66+
type NestedObject = DeepCopy<{ map: Map<string, string>; list: List<string>; set: Set<string>; }>;
67+
68+
// $ExpectType { map: { [x: string]: string; }; }
69+
type NestedMap = DeepCopy<Map<'map', Map<string, string>>>;
70+
}

type-definitions/ts-tests/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
// TypeScript Version: 2.2
1+
// Minimum TypeScript Version: 4.5
22
/* tslint:disable:no-useless-files */

type-definitions/ts-tests/list.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,16 @@ import {
445445
List<number>().asImmutable();
446446
}
447447

448+
{
449+
// #toJS / #toJSON
450+
451+
// $ExpectType number[][]
452+
List<List<number>>().toJS();
453+
454+
// $ExpectType List<number>[]
455+
List<List<number>>().toJSON();
456+
}
457+
448458
{
449459
// # for of loops
450460
const list = List([1, 2, 3, 4]);

type-definitions/ts-tests/map.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,3 +488,10 @@ import { Map, List } from 'immutable';
488488
// $ExpectType Map<number, number>
489489
Map<number, number>().asImmutable();
490490
}
491+
492+
{
493+
// #toJS
494+
495+
// $ExpectType { [x: string]: number; [x: number]: number; [x: symbol]: number; }
496+
Map<number, number>().toJS();
497+
}

type-definitions/ts-tests/record.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Record } from 'immutable';
1+
import { List, Map, Record, Set } from 'immutable';
22

33
{
44
// Factory
@@ -27,6 +27,9 @@ import { Record } from 'immutable';
2727
// $ExpectError
2828
pointXY.y = 10;
2929

30+
// $ExpectType { x: number; y: number; }
31+
pointXY.toJS();
32+
3033
class PointClass extends PointXY {
3134
setX(x: number) {
3235
return this.set('x', x);
@@ -53,6 +56,12 @@ import { Record } from 'immutable';
5356

5457
// $ExpectType PointClass
5558
point.setY(10);
59+
60+
// $ExpectType { x: number; y: number; }
61+
point.toJSON();
62+
63+
// $ExpectType { x: number; y: number; }
64+
point.toJS();
5665
}
5766

5867
{
@@ -65,3 +74,20 @@ import { Record } from 'immutable';
6574
// $ExpectError
6675
Record.getDescriptiveName({});
6776
}
77+
78+
{
79+
// Factory
80+
const WithMap = Record({
81+
map: Map({ a: 'A' }),
82+
list: List(['a']),
83+
set: Set(['a']),
84+
});
85+
86+
const withMap = WithMap();
87+
88+
// $ExpectType { map: Map<string, string>; list: List<string>; set: Set<string>; }
89+
withMap.toJSON();
90+
91+
// $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; }
92+
withMap.toJS();
93+
}

type-definitions/ts-tests/set.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,13 @@ import { Set, Map } from 'immutable';
282282
// $ExpectType Set<number>
283283
Set<number>().asImmutable();
284284
}
285+
286+
{
287+
// #toJS / #toJJSON
288+
289+
// $ExpectType number[][]
290+
Set<Set<number>>().toJS();
291+
292+
// $ExpectType Set<number>[]
293+
Set<Set<number>>().toJSON();
294+
}

0 commit comments

Comments
 (0)