Skip to content

Commit

Permalink
manage to handle toJS circular reference. (#1932)
Browse files Browse the repository at this point in the history
  • Loading branch information
jdeniau committed Aug 23, 2023
1 parent 3dfdf73 commit 0224bd0
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 21 deletions.
12 changes: 5 additions & 7 deletions type-definitions/immutable.d.ts
Expand Up @@ -100,27 +100,25 @@ declare namespace Immutable {
export type DeepCopy<T> = T extends Record<infer R>
? // convert Record to DeepCopy plain JS object
{
[key in keyof R]: R[key] extends object ? unknown : R[key];
[key in keyof R]: DeepCopy<R[key]>;
}
: T extends Collection.Keyed<infer KeyedKey, infer V>
? // convert KeyedCollection to DeepCopy plain JS object
{
[key in KeyedKey extends string | number | symbol
? KeyedKey
: string]: V extends object ? unknown : V;
: string]: DeepCopy<V>;
}
: // convert IndexedCollection or Immutable.Set to DeepCopy plain JS array
T extends Collection<infer _, infer V>
? Array<V extends object ? unknown : 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<V extends object ? unknown : V>
? Array<DeepCopy<V>>
: T extends object // plain JS object are converted deeply
? {
[ObjectKey in keyof T]: T[ObjectKey] extends object
? unknown
: T[ObjectKey];
[ObjectKey in keyof T]: DeepCopy<T[ObjectKey]>;
}
: // other case : should be kept as is
T;
Expand Down
16 changes: 5 additions & 11 deletions type-definitions/ts-tests/deepCopy.ts
Expand Up @@ -23,12 +23,10 @@ import { List, Map, Record, Set, Seq, DeepCopy, Collection } from 'immutable';
// $ExpectType { [x: string]: string; }
type StringKey = DeepCopy<Map<string, string>>;

// should be `{ [x: string]: object; }` but there is an issue with circular references
// $ExpectType { [x: string]: unknown; }
// $ExpectType { [x: string]: object; }
type ObjectKey = DeepCopy<Map<object, object>>;

// should be `{ [x: string]: object; [x: number]: object; }` but there is an issue with circular references
// $ExpectType { [x: string]: unknown; [x: number]: unknown; }
// $ExpectType { [x: string]: object; [x: number]: object; }
type MixedKey = DeepCopy<Map<object | number, object>>;

// $ExpectType string[]
Expand Down Expand Up @@ -57,12 +55,10 @@ import { List, Map, Record, Set, Seq, DeepCopy, Collection } from 'immutable';
{
// Nested

// should be `{ map: { [x: string]: string; }; list: string[]; set: string[]; }` but there is an issue with circular references
// $ExpectType { map: unknown; list: unknown; set: unknown; }
// $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; }
type NestedObject = DeepCopy<{ map: Map<string, string>; list: List<string>; set: Set<string>; }>;

// should be `{ map: { [x: string]: string; }; }`, but there is an issue with circular references
// $ExpectType { map: unknown; }
// $ExpectType { map: { [x: string]: string; }; }
type NestedMap = DeepCopy<Map<'map', Map<string, string>>>;
}

Expand All @@ -72,8 +68,6 @@ import { List, Map, Record, Set, Seq, DeepCopy, Collection } from 'immutable';
type Article = Record<{ title: string; tag: Tag; }>;
type Tag = Record<{ name: string; article: Article; }>;

// should handle circular references here somehow
// $ExpectType { title: string; tag: unknown; }
// $ExpectType { title: string; tag: { name: string; article: any; }; }
type Circular = DeepCopy<Article>;
// ^?
}
30 changes: 27 additions & 3 deletions type-definitions/ts-tests/record.ts
@@ -1,4 +1,4 @@
import { List, Map, Record, Set } from 'immutable';
import { List, Map, Record, RecordOf, Set } from 'immutable';

{
// Factory
Expand Down Expand Up @@ -88,7 +88,31 @@ import { List, Map, Record, Set } from 'immutable';
// $ExpectType { map: Map<string, string>; list: List<string>; set: Set<string>; }
withMap.toJSON();

// should be `{ map: { [x: string]: string; }; list: string[]; set: string[]; }` but there is an issue with circular references
// $ExpectType { map: unknown; list: unknown; set: unknown; }
// $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; }
withMap.toJS();
}

{
// optional properties

interface Size { distance: string; }

const Line = Record<{ size?: Size, color?: string }>({ size: undefined, color: 'red' });

const line = Line({});

// $ExpectType { size?: { distance: string; } | undefined; color?: string | undefined; }
line.toJS();
}

{
// similar properties, but one is optional. See https://github.com/immutable-js/immutable-js/issues/1930

interface Id { value: string; }

type A = RecordOf<{ id: Id }>;
type B = RecordOf<{ id?: Id }>;

const a: A = null as any;
const b: B = a;
}

0 comments on commit 0224bd0

Please sign in to comment.