10000 Overhaul flow type definitions for Record (#1104) · immutable-js/immutable-js@b20892d · GitHub
[go: up one dir, main page]

Skip to content

Commit b20892d

Browse files
authored
Overhaul flow type definitions for Record (#1104)
Fixes #992 Closes #876
1 parent fac6c46 commit b20892d

File tree

7 files changed

+147
-22
lines changed

7 files changed

+147
-22
lines changed

dist/immutable-nonambient.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@
15371537
*
15381538
* @alias toJSON
15391539
*/
1540-
toJS(): any;
1540+
toJS(): Object;
15411541

15421542
/**
15431543
* Shallowly converts this Record to equivalent JS.

dist/immutable.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@ declare module Immutable {
15371537
*
15381538
* @alias toJSON
15391539
*/
1540-
toJS(): any;
1540+
toJS(): Object;
15411541

15421542
/**
15431543
* Shallowly converts this Record to equivalent JS.

dist/immutable.js.flow

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,16 +1033,50 @@ declare class Stack<+T> extends IndexedCollection<T> {
10331033
declare function Range(start?: number, end?: number, step?: number): IndexedSeq<number>;
10341034
declare function Repeat<T>(value: T, times?: number): IndexedSeq<T>;
10351035

1036-
// TODO: Once flow can extend normal Objects we can change this back to actually reflect Record behavior.
1037-
// For now fallback to any to not break existing code
1038-
declare class Record<T: Object> {
1039-
static <T: Object>(spec: T, name?: string): /*T & Record<T>*/any;
1036+
declare class Record {
1037+
static <Values>(spec: Values, name?: string): RecordClass<Values>;
1038+
constructor<Values>(spec: Values, name?: string): RecordClass<Values>;
10401039

1041-
static getDescriptiveName(record: Record<any>): string;
1040+
static getDescriptiveName(record: RecordInstance<*>): string;
1041+
}
1042+
1043+
declare interface RecordClass<T> {
1044+
(values: $Shape<T> | ESIterable<[string, any]>): RecordInstance<T> & T;
1045+
new (values: $Shape<T> | ESIterable<[string, any]>): RecordInstance<T> & T;
1046+
}
1047+
1048+
declare class RecordInstance<T: Object> {
1049+
size: number;
1050+
1051+
has(key: string): boolean;
1052+
get<K: $Keys<T>>(key: K): /*T[K]*/any;
1053+
1054+
equals(other: any): boolean;
1055+
hashCode(): number;
1056+
1057+
set<K: $Keys<T>>(key: K, value: /*T[K]*/any): this;
1058+
update<K: $Keys<T>>(key: K, updater: (value: /*T[K]*/any) => /*T[K]*/any): this;
1059+
merge(...iterables: Array<$Shape<T> | ESIterable<[string, any]>>): this;
1060+
mergeDeep(...iterables: Array<$Shape<T> | ESIterable<[string, any]>>): this;
1061+
1062+
delete(key: $Keys<T>): this;
1063+
remove(key: $Keys<T>): this;
1064+
clear(): this;
1065+
1066+
setIn(keyPath: ESIterable<any>, value: any): this;
1067+
updateIn(keyPath: ESIterable<any>, updater: (value: any) => any): this;
1068+
mergeIn(keyPath: ESIterable<any>, ...iterables: Array<any>): this;
1069+
mergeDeepIn(keyPath: ESIterable<any>, ...iterables: Array<any>): this;
1070+
deleteIn(keyPath: ESIterable<any>): this;
1071+
removeIn(keyPath: ESIterable<any>): this;
1072+
1073+
toJS(): T;
1074+
toObject(): T;
1075+
withMutations(mutator: (mutable: this) => this | void): this;
1076+
asMutable(): this;
1077+
asImmutable(): this;
10421078

1043-
get<A>(key: $Keys<T>): A;
1044-
set<A>(key: $Keys<T>, value: A): /*T & Record<T>*/this;
1045-
remove(key: $Keys<T>): /*T & Record<T>*/this;
1079+
@@iterator(): Iterator<[$Keys<T>, any]>;
10461080
}
10471081

10481082
declare function fromJS(json: mixed, reviver?: (k: any, v: Iterable<any, any>) => any): any;

type-definitions/Immutable.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@ declare module Immutable {
15371537
*
15381538
* @alias toJSON
15391539
*/
1540-
toJS(): any;
1540+
toJS(): Object;
15411541

15421542
/**
15431543
* Shallowly converts this Record to equivalent JS.

type-definitions/immutable.js.flow

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,16 +1033,50 @@ declare class Stack<+T> extends IndexedCollection<T> {
10331033
declare function Range(start?: number, end?: number, step?: number): IndexedSeq<number>;
10341034
declare function Repeat<T>(value: T, times?: number): IndexedSeq<T>;
10351035

1036-
// TODO: Once flow can extend normal Objects we can change this back to actually reflect Record behavior.
1037-
// For now fallback to any to not break existing code
1038-
declare class Record<T: Object> {
1039-
static <T: Object>(spec: T, name?: string): /*T & Record<T>*/any;
1036+
declare class Record {
1037+
static <Values>(spec: Values, name?: string): RecordClass<Values>;
1038+
constructor<Values>(spec: Values, name?: string): RecordClass<Values>;
10401039

1041-
static getDescriptiveName(record: Record<any>): string;
1040+
static getDescriptiveName(record: RecordInstance<*>): string;
1041+
}
1042+
1043+
declare interface RecordClass<T> {
1044+
(values: $Shape<T> | ESIterable<[string, any]>): RecordInstance<T> & T;
1045+
new (values: $Shape<T> | ESIterable<[string, any]>): RecordInstance<T> & T;
1046+
}
1047+
1048+
declare class RecordInstance<T: Object> {
1049+
size: number;
1050+
1051+
has(key: string): boolean;
1052+
get<K: $Keys<T>>(key: K): /*T[K]*/any;
1053+
1054+
equals(other: any): boolean;
1055+
hashCode(): number;
1056+
1057+
set<K: $Keys<T>>(key: K, value: /*T[K]*/any): this;
1058+
update<K: $Keys<T>>(key: K, updater: (value: /*T[K]*/any) => /*T[K]*/any): this;
1059+
merge(...iterables: Array<$Shape<T> | ESIterable<[string, any]>>): this;
1060+
mergeDeep(...iterables: Array<$Shape<T> | ESIterable<[string, any]>>): this;
1061+
1062+
delete(key: $Keys<T>): this;
1063+
remove(key: $Keys<T>): this;
1064+
clear(): this;
1065+
1066+
setIn(keyPath: ESIterable<any>, value: any): this;
1067+
updateIn(keyPath: ESIterable<any>, updater: (value: any) => any): this;
1068+
mergeIn(keyPath: ESIterable<any>, ...iterables: Array<any>): this;
1069+
mergeDeepIn(keyPath: ESIterable<any>, ...iterables: Array<any>): this;
1070+
deleteIn(keyPath: ESIterable<any>): this;
1071+
removeIn(keyPath: ESIterable<any>): this;
1072+
1073+
toJS(): T;
1074+
toObject(): T;
1075+
withMutations(mutator: (mutable: this) => this | void): this;
1076+
asMutable(): this;
1077+
asImmutable(): this;
10421078

1043-
get<A>(key: $Keys<T>): A;
1044-
set<A>(key: $Keys<T>, value: A): /*T & Record<T>*/this;
1045-
remove(key: $Keys<T>): /*T & Record<T>*/this;
1079+
@@iterator(): Iterator<[$Keys<T>, any]>;
10461080
}
10471081

10481082
declare function fromJS(json: mixed, reviver?: (k: any, v: Iterable<any, any>) => any): any;

type-definitions/tests/immutable-flow.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,3 @@ numberStack = Stack(['a']).flatten()
765765
{ const stringSequence: IndexedSeq<string> = Repeat(0, 1) }
766766
// $ExpectError
767767
{ const stringSequence: IndexedSeq<string> = Range(0, 0, 0) }
768-
769-
/* Record */
770-
// TODO

type-definitions/tests/record.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* @flow
3+
*/
4+
5+
// Some tests look like they are repeated in order to avoid false positives.
6+
// Flow might not complain about an instance of (what it thinks is) T to be assigned to T<K, V>
7+
8+
import { Record } from '../../';
9+
10+
const Point2 = Record({x:0, y:0});
11+
const Point3 = Record({x:0, y:0, z:0});
12+
const GeoPoint = Record({lat:(null: ?number), lon:(null: ?number)});
13+
14+
let origin2 = Point2({});
15+
let origin3 = Point3({});
16+
let geo = GeoPoint({lat:34});
17+
// $ExpectError
18+
const mistake = Point2({x:'string'});
19+
origin3 = GeoPoint({lat:34})
20+
geo = Point3({});
21+
22+
const px = origin2.get('x');
23+
const px2 = origin2.x;
24+
// $ExpectError
25+
const pz = origin2.get('z');
26+
// $ExpectError
27+
const pz2 = origin2.z;
28+
29+
origin2.set('x', 4);
30+
// Note: this should be an error, but Flow does not yet support index types.
31+
origin2.set('x', 'string');
32+
// $ExpectError
33+
origin2.set('z', 3);
34+
35+
const name: string = Record.getDescriptiveName(origin2);
36+
// $ExpectError
37+
const name: string = Record.getDescriptiveName({});
38+
39+
// Note: need to cast through any when extending Records as if they ere classes
40+
class ABClass extends (Record({a:1, b:2}): any) {
41+
setA(a: number) {
42+
return this.set('a', a);
43+
}
44+
45+
setB(b: number) {
46+
return this.set('b', b);
47+
}
48+
}
49+
50+
var t1 = new ABClass({a: 1});
51+
var t2 = t1.setA(3);
52+
var t3 = t2.setB(10);
53+
// Note: flow does not check extended Record classes yet
54+
var t4 = t2.setC(10);
55+
56+
var t1a = t1.a;
57+
// Note: flow does not check extended Record classes yet
58+
var t1a: string = t1.a;
59+
// Note: flow does not check extended Record classes yet
60+
var t1c = t1.c;

0 commit comments

Comments
 (0)
0