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

Rollback toJS type due to circular reference error #1958

Merged
merged 1 commit into from Aug 25, 2023
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
18 changes: 15 additions & 3 deletions type-definitions/immutable.d.ts
Expand Up @@ -91,6 +91,16 @@
*/

declare namespace Immutable {
/** @ignore */
type OnlyObject<T> = Extract<T, object>;

/** @ignore */
type ContainObject<T> = OnlyObject<T> extends object
? OnlyObject<T> extends never
? false
: true
: false;

/**
* @ignore
*
Expand All @@ -100,14 +110,14 @@ declare namespace Immutable {
export type DeepCopy<T> = T extends Record<infer R>
? // convert Record to DeepCopy plain JS object
{
[key in keyof R]: DeepCopy<R[key]>;
[key in keyof R]: ContainObject<R[key]> extends true ? unknown : 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]: DeepCopy<V>;
: string]: V extends object ? unknown : V;
}
: // convert IndexedCollection or Immutable.Set to DeepCopy plain JS array
T extends Collection<infer _, infer V>
Expand All @@ -118,7 +128,9 @@ declare namespace Immutable {
? Array<DeepCopy<V>>
: T extends object // plain JS object are converted deeply
? {
[ObjectKey in keyof T]: DeepCopy<T[ObjectKey]>;
[ObjectKey in keyof T]: ContainObject<T[ObjectKey]> extends true
? unknown
: T[ObjectKey];
}
: // other case : should be kept as is
T;
Expand Down
59 changes: 54 additions & 5 deletions type-definitions/ts-tests/deepCopy.ts
Expand Up @@ -23,10 +23,12 @@ import { List, Map, Record, Set, Seq, DeepCopy, Collection } from 'immutable';
// $ExpectType { [x: string]: string; }
type StringKey = DeepCopy<Map<string, string>>;

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

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

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

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

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

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

// $ExpectType { title: string; tag: { name: string; article: any; }; }
// should handle circular references here somehow
// $ExpectType { title: string; tag: unknown; }
type Circular = DeepCopy<Article>;
}

{
// Circular references #1957

class Foo1 extends Record<{
foo: undefined | Foo1;
}>({
foo: undefined
}) {
}

class Foo2 extends Record<{
foo?: Foo2;
}>({
foo: undefined
}) {
}

class Foo3 extends Record<{
foo: null | Foo3;
}>({
foo: null
}) {
}

// $ExpectType { foo: unknown; }
type DeepFoo1 = DeepCopy<Foo1>;

// $ExpectType { foo?: unknown; }
type DeepFoo2 = DeepCopy<Foo2>;

// $ExpectType { foo: unknown; }
type DeepFoo3 = DeepCopy<Foo3>;

class FooWithList extends Record<{
foos: undefined | List<FooWithList>;
}>({
foos: undefined
}) {
}

// $ExpectType { foos: unknown; }
type DeepFooList = DeepCopy<FooWithList>;
}
6 changes: 4 additions & 2 deletions type-definitions/ts-tests/record.ts
Expand Up @@ -88,7 +88,8 @@ import { List, Map, Record, RecordOf, Set } from 'immutable';
// $ExpectType { map: Map<string, string>; list: List<string>; set: Set<string>; }
withMap.toJSON();

// $ExpectType { map: { [x: string]: string; }; list: string[]; set: string[]; }
// 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; }
withMap.toJS();
}

Expand All @@ -101,7 +102,8 @@ import { List, Map, Record, RecordOf, Set } from 'immutable';

const line = Line({});

// $ExpectType { size?: { distance: string; } | undefined; color?: string | undefined; }
// should be { size?: { distance: string; } | undefined; color?: string | undefined; } but there is an issue with circular references
// $ExpectType { size?: unknown; color?: string | undefined; }
line.toJS();
}

Expand Down