Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better type for Record.toJS #1917

Merged
merged 4 commits into from Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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();
}