From c6c79daf591462b910f06b38b7d1b23225298e7e Mon Sep 17 00:00:00 2001 From: Julien Deniau Date: Mon, 19 Dec 2022 22:27:34 +0100 Subject: [PATCH 1/4] better type for Record.toJS --- type-definitions/immutable.d.ts | 22 ++++++++++- type-definitions/ts-tests/deepCopy.ts | 57 +++++++++++++++++++++++++++ type-definitions/ts-tests/index.d.ts | 2 +- type-definitions/ts-tests/record.ts | 28 ++++++++++++- 4 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 type-definitions/ts-tests/deepCopy.ts diff --git a/type-definitions/immutable.d.ts b/type-definitions/immutable.d.ts index db4816ec7..03c4316b5 100644 --- a/type-definitions/immutable.d.ts +++ b/type-definitions/immutable.d.ts @@ -91,6 +91,26 @@ */ declare namespace Immutable { + export type DeepCopy = T extends Collection.Keyed + ? // convert KeyedCollection to DeepCopy plain JS object + globalThis.Record< + 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 +2686,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. diff --git a/type-definitions/ts-tests/deepCopy.ts b/type-definitions/ts-tests/deepCopy.ts new file mode 100644 index 000000000..9a6879338 --- /dev/null +++ b/type-definitions/ts-tests/deepCopy.ts @@ -0,0 +1,57 @@ +import { List, Map, Record, Set, 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; + // ^? +} + +{ + // Immutable first-level types + + // $ExpectType Record + type StringKey = DeepCopy>; + // ^? + + // $ExpectType Record + type ObjectKey = DeepCopy>; + // ^? + + // $ExpectType Record + type MixedKey = DeepCopy>; + // ^? + + // $ExpectType string[] + type ListDeepCopy = DeepCopy>; + // ^? + + // $ExpectType string[] + type SetDeepCopy = DeepCopy>; + // ^? +} + +{ + // Nested + + // $ExpectType { map: Record; list: string[]; set: string[]; } + type NestedObject = DeepCopy<{ map: Map; list: List; set: Set; }>; + // ^? + + // $ExpectType Record<"map", Record> + type NestedMap = DeepCopy>>; + // ^? +} diff --git a/type-definitions/ts-tests/index.d.ts b/type-definitions/ts-tests/index.d.ts index 9f2e612b0..3b651a6e4 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.1 /* tslint:disable:no-useless-files */ diff --git a/type-definitions/ts-tests/record.ts b/type-definitions/ts-tests/record.ts index ae54dc9e0..6898ad743 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: Record; list: string[]; set: string[]; } + withMap.toJS(); +} From 3e9d3b8f55a539dad434a309e0ead559b8f0566f Mon Sep 17 00:00:00 2001 From: Julien Deniau Date: Mon, 19 Dec 2022 22:53:42 +0100 Subject: [PATCH 2/4] List and Set toJS type --- type-definitions/immutable.d.ts | 8 ++++---- type-definitions/ts-tests/list.ts | 10 ++++++++++ type-definitions/ts-tests/set.ts | 10 ++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/type-definitions/immutable.d.ts b/type-definitions/immutable.d.ts index 03c4316b5..becdb5de8 100644 --- a/type-definitions/immutable.d.ts +++ b/type-definitions/immutable.d.ts @@ -2988,7 +2988,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. @@ -3163,7 +3163,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. @@ -3655,7 +3655,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. @@ -3966,7 +3966,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. 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/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(); +} From 1914012a735ae9eea5cbffe32800461b9ee4b6fa Mon Sep 17 00:00:00 2001 From: Julien Deniau Date: Thu, 22 Dec 2022 16:07:48 +0100 Subject: [PATCH 3/4] clean, use TS 4.5 for tests --- type-definitions/immutable.d.ts | 23 +++++++++------- type-definitions/ts-tests/deepCopy.ts | 39 ++++++++++++++++++--------- type-definitions/ts-tests/index.d.ts | 2 +- type-definitions/ts-tests/map.ts | 7 +++++ type-definitions/ts-tests/record.ts | 2 +- 5 files changed, 48 insertions(+), 25 deletions(-) diff --git a/type-definitions/immutable.d.ts b/type-definitions/immutable.d.ts index becdb5de8..d662fd96b 100644 --- a/type-definitions/immutable.d.ts +++ b/type-definitions/immutable.d.ts @@ -93,10 +93,11 @@ declare namespace Immutable { export type DeepCopy = T extends Collection.Keyed ? // convert KeyedCollection to DeepCopy plain JS object - globalThis.Record< - KeyedKey extends string | number | symbol ? KeyedKey : string, - DeepCopy - > + { + [key in KeyedKey extends string | number | symbol + ? KeyedKey + : string]: DeepCopy; + } : // convert IndexedCollection or Immutable.Set to DeepCopy plain JS array T extends Collection ? Array> @@ -2846,14 +2847,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. @@ -3473,14 +3474,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. @@ -4228,7 +4229,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. @@ -4236,7 +4239,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 index 9a6879338..3012e0ffc 100644 --- a/type-definitions/ts-tests/deepCopy.ts +++ b/type-definitions/ts-tests/deepCopy.ts @@ -1,4 +1,4 @@ -import { List, Map, Record, Set, DeepCopy, Collection } from 'immutable'; +import { List, Map, Record, Set, Seq, DeepCopy, Collection } from 'immutable'; { // Basic types @@ -18,40 +18,53 @@ import { List, Map, Record, Set, DeepCopy, Collection } from 'immutable'; // $ExpectType string[] type Test = DeepCopy; // ^? + + // $ExpectType number[] + type Keyed = DeepCopy>; + // ^? } { // Immutable first-level types - // $ExpectType Record + // $ExpectType { [x: string]: string; } type StringKey = DeepCopy>; - // ^? - // $ExpectType Record + // $ExpectType { [x: string]: object; } type ObjectKey = DeepCopy>; - // ^? - // $ExpectType Record + // $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: Record; list: string[]; set: string[]; } + // $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; } type NestedObject = DeepCopy<{ map: Map; list: List; set: Set; }>; - // ^? - // $ExpectType Record<"map", Record> + // $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 3b651a6e4..11eec1bf4 100644 --- a/type-definitions/ts-tests/index.d.ts +++ b/type-definitions/ts-tests/index.d.ts @@ -1,2 +1,2 @@ -// Minimum TypeScript Version: 4.1 +// Minimum TypeScript Version: 4.5 /* tslint:disable:no-useless-files */ 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 6898ad743..7a626e438 100644 --- a/type-definitions/ts-tests/record.ts +++ b/type-definitions/ts-tests/record.ts @@ -88,6 +88,6 @@ import { List, Map, Record, Set } from 'immutable'; // $ExpectType { map: Map; list: List; set: Set; } withMap.toJSON(); - // $ExpectType { map: Record; list: string[]; set: string[]; } + // $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; } withMap.toJS(); } From 06501439463fb3d9439ae64ddfcb090cc33e0824 Mon Sep 17 00:00:00 2001 From: Julien Deniau Date: Thu, 22 Dec 2022 16:24:10 +0100 Subject: [PATCH 4/4] ignore helper type --- type-definitions/immutable.d.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/type-definitions/immutable.d.ts b/type-definitions/immutable.d.ts index d662fd96b..7adfdeb38 100644 --- a/type-definitions/immutable.d.ts +++ b/type-definitions/immutable.d.ts @@ -91,6 +91,9 @@ */ declare namespace Immutable { + /** + * @ignore + */ export type DeepCopy = T extends Collection.Keyed ? // convert KeyedCollection to DeepCopy plain JS object {