diff --git a/type-definitions/immutable.d.ts b/type-definitions/immutable.d.ts index db4816ec7..7adfdeb38 100644 --- a/type-definitions/immutable.d.ts +++ b/type-definitions/immutable.d.ts @@ -91,6 +91,30 @@ */ declare namespace Immutable { + /** + * @ignore + */ + export type DeepCopy = T extends Collection.Keyed + ? // convert KeyedCollection to DeepCopy plain JS object + { + [key in KeyedKey extends string | number | symbol + ? KeyedKey + : string]: DeepCopy; + } + : // convert IndexedCollection or Immutable.Set to DeepCopy plain JS array + T extends Collection + ? Array> + : T extends string | number // Iterable scalar types : should be kept as is + ? T + : T extends Iterable // Iterable are converted to plain JS array + ? Array> + : T extends object // plain JS object are converted deeply + ? { + [ObjectKey in keyof T]: DeepCopy; + } + : // other case : should be kept as is + T; + /** * Lists are ordered indexed dense collections, much like a JavaScript * Array. @@ -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; /** * Shallowly converts this Record to equivalent native JavaScript Object. @@ -2826,14 +2850,14 @@ declare namespace Immutable { * * Converts keys to Strings. */ - toJS(): { [key: string]: unknown }; + toJS(): { [key in string | number | symbol]: DeepCopy }; /** * 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. @@ -2968,7 +2992,7 @@ declare namespace Immutable { /** * Deeply converts this Indexed Seq to equivalent native JavaScript Array. */ - toJS(): Array; + toJS(): Array>; /** * Shallowly converts this Indexed Seq to equivalent native JavaScript Array. @@ -3143,7 +3167,7 @@ declare namespace Immutable { /** * Deeply converts this Set Seq to equivalent native JavaScript Array. */ - toJS(): Array; + toJS(): Array>; /** * Shallowly converts this Set Seq to equivalent native JavaScript Array. @@ -3453,14 +3477,14 @@ declare namespace Immutable { * * Converts keys to Strings. */ - toJS(): { [key: string]: unknown }; + toJS(): { [key in string | number | symbol]: DeepCopy }; /** * 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. @@ -3635,7 +3659,7 @@ declare namespace Immutable { /** * Deeply converts this Indexed collection to equivalent native JavaScript Array. */ - toJS(): Array; + toJS(): Array>; /** * Shallowly converts this Indexed collection to equivalent native JavaScript Array. @@ -3946,7 +3970,7 @@ declare namespace Immutable { /** * Deeply converts this Set collection to equivalent native JavaScript Array. */ - toJS(): Array; + toJS(): Array>; /** * Shallowly converts this Set collection to equivalent native JavaScript Array. @@ -4208,7 +4232,9 @@ declare namespace Immutable { * `Collection.Indexed`, and `Collection.Set` become `Array`, while * `Collection.Keyed` become `Object`, converting keys to Strings. */ - toJS(): Array | { [key: string]: unknown }; + toJS(): + | Array> + | { [key in string | number | symbol]: DeepCopy }; /** * Shallowly converts this Collection to equivalent native JavaScript Array or Object. @@ -4216,7 +4242,7 @@ declare namespace Immutable { * `Collection.Indexed`, and `Collection.Set` become `Array`, while * `Collection.Keyed` become `Object`, converting keys to Strings. */ - toJSON(): Array | { [key: string]: V }; + toJSON(): Array | { [key in string | number | symbol]: V }; /** * Shallowly converts this collection to an Array. diff --git a/type-definitions/ts-tests/deepCopy.ts b/type-definitions/ts-tests/deepCopy.ts new file mode 100644 index 000000000..3012e0ffc --- /dev/null +++ b/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; + // ^? + + // $ExpectType number[] + type Keyed = DeepCopy>; + // ^? +} + +{ + // Immutable first-level types + + // $ExpectType { [x: string]: string; } + type StringKey = DeepCopy>; + + // $ExpectType { [x: string]: object; } + type ObjectKey = DeepCopy>; + + // $ExpectType { [x: string]: object; [x: number]: object; } + type MixedKey = DeepCopy>; + + // $ExpectType string[] + type ListDeepCopy = DeepCopy>; + + // $ExpectType string[] + type SetDeepCopy = DeepCopy>; +} + +{ + // Keyed + + // $ExpectType { [x: string]: number; } + type Keyed = DeepCopy>; + + // $ExpectType { [x: string]: number; [x: number]: number; } + type KeyedMixed = DeepCopy>; + + // $ExpectType { [x: string]: number; [x: number]: number; } + type KeyedSeqMixed = DeepCopy>; + + // $ExpectType { [x: string]: number; [x: number]: number; } + type MapMixed = DeepCopy>; +} + +{ + // Nested + + // $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; } + type NestedObject = DeepCopy<{ map: Map; list: List; set: Set; }>; + + // $ExpectType { map: { [x: string]: string; }; } + type NestedMap = DeepCopy>>; +} diff --git a/type-definitions/ts-tests/index.d.ts b/type-definitions/ts-tests/index.d.ts index 9f2e612b0..11eec1bf4 100644 --- a/type-definitions/ts-tests/index.d.ts +++ b/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 */ diff --git a/type-definitions/ts-tests/list.ts b/type-definitions/ts-tests/list.ts index eef31b9e9..e1738e9d9 100644 --- a/type-definitions/ts-tests/list.ts +++ b/type-definitions/ts-tests/list.ts @@ -445,6 +445,16 @@ import { List().asImmutable(); } +{ + // #toJS / #toJSON + + // $ExpectType number[][] + List>().toJS(); + + // $ExpectType List[] + List>().toJSON(); +} + { // # for of loops const list = List([1, 2, 3, 4]); diff --git a/type-definitions/ts-tests/map.ts b/type-definitions/ts-tests/map.ts index ddfe59093..3fffffeda 100644 --- a/type-definitions/ts-tests/map.ts +++ b/type-definitions/ts-tests/map.ts @@ -488,3 +488,10 @@ import { Map, List } from 'immutable'; // $ExpectType Map Map().asImmutable(); } + +{ + // #toJS + + // $ExpectType { [x: string]: number; [x: number]: number; [x: symbol]: number; } + Map().toJS(); +} diff --git a/type-definitions/ts-tests/record.ts b/type-definitions/ts-tests/record.ts index ae54dc9e0..7a626e438 100644 --- a/type-definitions/ts-tests/record.ts +++ b/type-definitions/ts-tests/record.ts @@ -1,4 +1,4 @@ -import { Record } from 'immutable'; +import { List, Map, Record, Set } from 'immutable'; { // Factory @@ -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); @@ -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(); } { @@ -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; list: List; set: Set; } + withMap.toJSON(); + + // $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; } + withMap.toJS(); +} diff --git a/type-definitions/ts-tests/set.ts b/type-definitions/ts-tests/set.ts index 3f295a094..596fb0b1b 100644 --- a/type-definitions/ts-tests/set.ts +++ b/type-definitions/ts-tests/set.ts @@ -282,3 +282,13 @@ import { Set, Map } from 'immutable'; // $ExpectType Set Set().asImmutable(); } + +{ + // #toJS / #toJJSON + + // $ExpectType number[][] + Set>().toJS(); + + // $ExpectType Set[] + Set>().toJSON(); +}