Skip to content

Commit

Permalink
better type for Record.toJS (#1917)
Browse files Browse the repository at this point in the history
  • Loading branch information
jdeniau committed Dec 22, 2022
1 parent cfd7957 commit ffb3ee8
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 13 deletions.
48 changes: 37 additions & 11 deletions type-definitions/immutable.d.ts
Expand Up @@ -91,6 +91,30 @@
*/

declare namespace Immutable {
/**
* @ignore
*/
export type DeepCopy<T> = T extends Collection.Keyed<infer KeyedKey, infer V>
? // convert KeyedCollection to DeepCopy plain JS object
{
[key in KeyedKey extends string | number | symbol
? KeyedKey
: string]: DeepCopy<V>;
}
: // convert IndexedCollection or Immutable.Set to DeepCopy plain JS array
T extends Collection<infer _, infer V>
? Array<DeepCopy<V>>
: T extends string | number // Iterable scalar types : should be kept as is
? T
: T extends Iterable<infer V> // Iterable are converted to plain JS array
? Array<DeepCopy<V>>
: T extends object // plain JS object are converted deeply
? {
[ObjectKey in keyof T]: DeepCopy<T[ObjectKey]>;
}
: // other case : should be kept as is
T;

/**
* Lists are ordered indexed dense collections, much like a JavaScript
* Array.
Expand Down Expand Up @@ -2666,7 +2690,7 @@ declare namespace Immutable {
* Note: This method may not be overridden. Objects with custom
* serialization to plain JS may override toJSON() instead.
*/
toJS(): { [K in keyof TProps]: unknown };
toJS(): DeepCopy<TProps>;

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

/**
* Shallowly converts this Keyed Seq to equivalent native JavaScript Object.
*
* Converts keys to Strings.
*/
toJSON(): { [key: string]: V };
toJSON(): { [key in string | number | symbol]: V };

/**
* Shallowly converts this collection to an Array.
Expand Down Expand Up @@ -2968,7 +2992,7 @@ declare namespace Immutable {
/**
* Deeply converts this Indexed Seq to equivalent native JavaScript Array.
*/
toJS(): Array<unknown>;
toJS(): Array<DeepCopy<T>>;

/**
* Shallowly converts this Indexed Seq to equivalent native JavaScript Array.
Expand Down Expand Up @@ -3143,7 +3167,7 @@ declare namespace Immutable {
/**
* Deeply converts this Set Seq to equivalent native JavaScript Array.
*/
toJS(): Array<unknown>;
toJS(): Array<DeepCopy<T>>;

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

/**
* Shallowly converts this Keyed collection to equivalent native JavaScript Object.
*
* Converts keys to Strings.
*/
toJSON(): { [key: string]: V };
toJSON(): { [key in string | number | symbol]: V };

/**
* Shallowly converts this collection to an Array.
Expand Down Expand Up @@ -3635,7 +3659,7 @@ declare namespace Immutable {
/**
* Deeply converts this Indexed collection to equivalent native JavaScript Array.
*/
toJS(): Array<unknown>;
toJS(): Array<DeepCopy<T>>;

/**
* Shallowly converts this Indexed collection to equivalent native JavaScript Array.
Expand Down Expand Up @@ -3946,7 +3970,7 @@ declare namespace Immutable {
/**
* Deeply converts this Set collection to equivalent native JavaScript Array.
*/
toJS(): Array<unknown>;
toJS(): Array<DeepCopy<T>>;

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

/**
* Shallowly converts this Collection to equivalent native JavaScript Array or Object.
*
* `Collection.Indexed`, and `Collection.Set` become `Array`, while
* `Collection.Keyed` become `Object`, converting keys to Strings.
*/
toJSON(): Array<V> | { [key: string]: V };
toJSON(): Array<V> | { [key in string | number | symbol]: V };

/**
* Shallowly converts this collection to an Array.
Expand Down
70 changes: 70 additions & 0 deletions type-definitions/ts-tests/deepCopy.ts
@@ -0,0 +1,70 @@
import { List, Map, Record, Set, Seq, DeepCopy, Collection } from 'immutable';

{
// Basic types

// $ExpectType { a: number; b: number; }
type Test = DeepCopy<{ a: number; b: number }>;
// ^?

// $ExpectType number
type TestA = Test['a'];
// ^?
}

{
// Iterables

// $ExpectType string[]
type Test = DeepCopy<string[]>;
// ^?

// $ExpectType number[]
type Keyed = DeepCopy<Collection.Indexed<number>>;
// ^?
}

{
// Immutable first-level types

// $ExpectType { [x: string]: string; }
type StringKey = DeepCopy<Map<string, string>>;

// $ExpectType { [x: string]: object; }
type ObjectKey = DeepCopy<Map<object, object>>;

// $ExpectType { [x: string]: object; [x: number]: object; }
type MixedKey = DeepCopy<Map<object | number, object>>;

// $ExpectType string[]
type ListDeepCopy = DeepCopy<List<string>>;

// $ExpectType string[]
type SetDeepCopy = DeepCopy<Set<string>>;
}

{
// Keyed

// $ExpectType { [x: string]: number; }
type Keyed = DeepCopy<Collection.Keyed<string, number>>;

// $ExpectType { [x: string]: number; [x: number]: number; }
type KeyedMixed = DeepCopy<Collection.Keyed<string | number, number>>;

// $ExpectType { [x: string]: number; [x: number]: number; }
type KeyedSeqMixed = DeepCopy<Seq.Keyed<string | number, number>>;

// $ExpectType { [x: string]: number; [x: number]: number; }
type MapMixed = DeepCopy<Map<string | number, number>>;
}

{
// Nested

// $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; }
type NestedObject = DeepCopy<{ map: Map<string, string>; list: List<string>; set: Set<string>; }>;

// $ExpectType { map: { [x: string]: string; }; }
type NestedMap = DeepCopy<Map<'map', Map<string, string>>>;
}
2 changes: 1 addition & 1 deletion type-definitions/ts-tests/index.d.ts
@@ -1,2 +1,2 @@
// TypeScript Version: 2.2
// Minimum TypeScript Version: 4.5
/* tslint:disable:no-useless-files */
10 changes: 10 additions & 0 deletions type-definitions/ts-tests/list.ts
Expand Up @@ -445,6 +445,16 @@ import {
List<number>().asImmutable();
}

{
// #toJS / #toJSON

// $ExpectType number[][]
List<List<number>>().toJS();

// $ExpectType List<number>[]
List<List<number>>().toJSON();
}

{
// # for of loops
const list = List([1, 2, 3, 4]);
Expand Down
7 changes: 7 additions & 0 deletions type-definitions/ts-tests/map.ts
Expand Up @@ -488,3 +488,10 @@ import { Map, List } from 'immutable';
// $ExpectType Map<number, number>
Map<number, number>().asImmutable();
}

{
// #toJS

// $ExpectType { [x: string]: number; [x: number]: number; [x: symbol]: number; }
Map<number, number>().toJS();
}
28 changes: 27 additions & 1 deletion type-definitions/ts-tests/record.ts
@@ -1,4 +1,4 @@
import { Record } from 'immutable';
import { List, Map, Record, Set } from 'immutable';

{
// Factory
Expand Down Expand Up @@ -27,6 +27,9 @@ import { Record } from 'immutable';
// $ExpectError
pointXY.y = 10;

// $ExpectType { x: number; y: number; }
pointXY.toJS();

class PointClass extends PointXY {
setX(x: number) {
return this.set('x', x);
Expand All @@ -53,6 +56,12 @@ import { Record } from 'immutable';

// $ExpectType PointClass
point.setY(10);

// $ExpectType { x: number; y: number; }
point.toJSON();

// $ExpectType { x: number; y: number; }
point.toJS();
}

{
Expand All @@ -65,3 +74,20 @@ import { Record } from 'immutable';
// $ExpectError
Record.getDescriptiveName({});
}

{
// Factory
const WithMap = Record({
map: Map({ a: 'A' }),
list: List(['a']),
set: Set(['a']),
});

const withMap = WithMap();

// $ExpectType { map: Map<string, string>; list: List<string>; set: Set<string>; }
withMap.toJSON();

// $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; }
withMap.toJS();
}
10 changes: 10 additions & 0 deletions type-definitions/ts-tests/set.ts
Expand Up @@ -282,3 +282,13 @@ import { Set, Map } from 'immutable';
// $ExpectType Set<number>
Set<number>().asImmutable();
}

{
// #toJS / #toJJSON

// $ExpectType number[][]
Set<Set<number>>().toJS();

// $ExpectType Set<number>[]
Set<Set<number>>().toJSON();
}

0 comments on commit ffb3ee8

Please sign in to comment.