Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e3729e6

Browse files
committed
RFC: Refactor Record. No longer a Collection. Faster guts.
This is a breaking change refactor of Record which no longer extends a collection type and therefore does not inherit sequence methods - which was a constant source of confusion and breakage. It also refactors the internals to no longer rely on a Map, but instead a fixed size List which should result in dramatically faster performance. This is also a breaking change as `delete()` (aka `remove()`) and `clear()` are no longer available - previously these methods reverted values to their default value, now that can be done with `set(k, undefined)`. This adds a predicate function `isRecord()` and also minorly refactors related functionality such as `Seq()` constructors. Fixes #505 Fixes #286
1 parent ce33aec commit e3729e6

17 files changed

+507
-388
lines changed

__tests__/Conversion.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ describe('Conversion', () => {
116116
'"b": "B"'+
117117
' }, '+
118118
'"emptyMap": OrderedMap {}, ' +
119-
'"point": Point { "x": 10, "y": 20 }, '+
119+
'"point": Point { x: 10, y: 20 }, '+
120120
'"string": "Hello", '+
121121
'"list": List [ 1, 2, 3 ]'+
122122
' }';
@@ -196,7 +196,7 @@ describe('Conversion', () => {
196196
Model.prototype.toJSON = function() { return 'model'; }
197197
expect(
198198
Map({ a: new Model() }).toJS()
199-
).toEqual({ "a": {} });
199+
).toEqual({ a: {} });
200200
expect(
201201
JSON.stringify(Map({ a: new Model() }))
202202
).toEqual('{"a":"model"}');

__tests__/MultiRequire.js

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ describe('MultiRequire', () => {
1717
expect(Immutable2.Iterable.isIterable(x)).toBe(true);
1818
});
1919

20+
it('detects records', () => {
21+
var R1 = Immutable1.Record({a: 1});
22+
var R2 = Immutable2.Record({a: 1});
23+
expect(Immutable1.isRecord(R2())).toBe(true);
24+
expect(Immutable2.isRecord(R1())).toBe(true);
25+
});
26+
2027
it('converts to JS when inter-nested', () => {
2128
var deep = Immutable1.Map({
2229
a: 1,

__tests__/Record.ts

+51-35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
///<reference path='../resources/jest.d.ts'/>
22

3-
import { Record, Seq } from '../';
3+
import { Record, Seq, isKeyed } from '../';
44

55
describe('Record', () => {
66

@@ -9,26 +9,22 @@ describe('Record', () => {
99

1010
var t1 = new MyType();
1111
var t2 = t1.set('a', 10);
12-
var t3 = t2.clear();
1312

1413
expect(t1 instanceof Record).toBe(true);
1514
expect(t1 instanceof MyType).toBe(true);
1615

17-
expect(t3 instanceof Record).toBe(true);
18-
expect(t3 instanceof MyType).toBe(true);
16+
expect(t2 instanceof Record).toBe(true);
17+
expect(t2 instanceof MyType).toBe(true);
1918

2019
expect(t1.get('a')).toBe(1);
2120
expect(t2.get('a')).toBe(10);
22-
23-
expect(t1.size).toBe(3);
24-
expect(t2.size).toBe(3);
2521
})
2622

2723
it('allows for a descriptive name', () => {
2824
var Person = Record({name: null}, 'Person');
2925

3026
var me = Person({ name: 'My Name' })
31-
expect(me.toString()).toEqual('Person { "name": "My Name" }');
27+
expect(me.toString()).toEqual('Person { name: "My Name" }');
3228
expect(Record.getDescriptiveName(me)).toEqual('Person');
3329
})
3430

@@ -52,27 +48,11 @@ describe('Record', () => {
5248
expect(t2).toBe(t1);
5349
});
5450

55-
it('has a fixed size and falls back to default values', () => {
51+
it('is a value type and equals other similar Records', () => {
5652
var MyType = Record({a:1, b:2, c:3});
57-
58-
var t1 = new MyType({a: 10, b:20});
59-
var t2 = new MyType({b: 20});
60-
var t3 = t1.remove('a');
61-
var t4 = t3.clear();
62-
63-
expect(t1.size).toBe(3);
64-
expect(t2.size).toBe(3);
65-
expect(t3.size).toBe(3);
66-
expect(t4.size).toBe(3);
67-
68-
expect(t1.get('a')).toBe(10);
69-
expect(t2.get('a')).toBe(1);
70-
expect(t3.get('a')).toBe(1);
71-
expect(t4.get('b')).toBe(2);
72-
73-
expect(t2.equals(t3)).toBe(true);
74-
expect(t2.equals(t4)).toBe(false);
75-
expect(t4.equals(new MyType())).toBe(true);
53+
var t1 = MyType({ a: 10 })
54+
var t2 = MyType({ a: 10, b: 2 })
55+
expect(t1.equals(t2));
7656
})
7757

7858
it('merges in Objects and other Records', () => {
@@ -167,22 +147,58 @@ describe('Record', () => {
167147
var warnings = [];
168148
console.warn = w => warnings.push(w);
169149

170-
var MyType1 = Record({size:0});
150+
// size is a safe key to use
151+
var MyType1 = Record({size:123});
171152
var t1 = MyType1();
172-
expect(warnings.length).toBe(1);
173-
expect(warnings[0]).toBe(
174-
'Cannot define Record with property "size" since that property name is part of the Record API.'
175-
);
153+
expect(warnings.length).toBe(0);
154+
expect(t1.size).toBe(123);
176155

156+
// get() is not safe to use
177157
var MyType2 = Record({get:0});
178158
var t2 = MyType2();
179-
expect(warnings.length).toBe(2);
180-
expect(warnings[1]).toBe(
159+
expect(warnings.length).toBe(1);
160+
expect(warnings[0]).toBe(
181161
'Cannot define Record with property "get" since that property name is part of the Record API.'
182162
);
183163
} finally {
184164
console.warn = realWarn;
185165
}
186166
})
187167

168+
it('can be converted to a keyed sequence', () => {
169+
var MyType = Record({a:0, b:0});
170+
var t1 = MyType({a:10, b:20});
171+
172+
var seq1 = t1.toSeq();
173+
expect(isKeyed(seq1)).toBe(true);
174+
expect(seq1.toJS()).toEqual({a:10, b:20});
175+
176+
var seq2 = Seq(t1)
177+
expect(isKeyed(seq2)).toBe(true);
178+
expect(seq2.toJS()).toEqual({a:10, b:20});
179+
180+
var seq3 = Seq.Keyed(t1)
181+
expect(isKeyed(seq3)).toBe(true);
182+
expect(seq3.toJS()).toEqual({a:10, b:20});
183+
184+
var seq4 = Seq.Indexed(t1)
185+
expect(isKeyed(seq4)).toBe(false);
186+
expect(seq4.toJS()).toEqual([['a', 10], ['b', 20]]);
187+
})
188+
189+
it('can be iterated over', () => {
190+
var MyType = Record({a:0, b:0});
191+
var t1 = MyType({a:10, b:20});
192+
193+
var entries = [];
194+
for (let entry of t1) {
195+
entries.push(entry);
196+
}
197+
198+
expect(entries).toEqual([
199+
[ 'a', 10 ],
200+
[ 'b', 20 ],
201+
])
202+
})
203+
188204
});

__tests__/RecordJS.js

-16
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,4 @@ describe('Record', () => {
4646
expect(t.soup()).toBe(6);
4747
expect(t2.soup()).toBe(204);
4848
});
49-
50-
it('can be cleared', () => {
51-
var MyType = Record({a:1, b:2, c:3});
52-
var t = new MyType({c:'cats'});
53-
54-
expect(t.c).toBe('cats');
55-
t = t.clear();
56-
expect(t.c).toBe(3);
57-
58-
var MyType2 = Record({d:4, e:5, f:6});
59-
var t2 = new MyType2({d:'dogs'});
60-
61-
expect(t2.d).toBe('dogs');
62-
t2 = t2.clear();
63-
expect(t2.d).toBe(4);
64-
});
6549
});

dist/immutable-nonambient.d.ts

+26-12
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@
191191
*/
192192
export function isOrdered(maybeOrdered: any): boolean;
193193

194+
/**
195+
* True if `maybeRecord` is an instance of a Record.
196+
*/
197+
export function isRecord(maybeRecord: any): maybeRecord is Record.Instance<any>;
198+
194199
/**
195200
* True if `maybeValue` is a JavaScript Object which has *both* `equals()`
196201
* and `hashCode()` methods.
@@ -1838,6 +1843,11 @@
18381843
has(key: string): boolean;
18391844
get<K extends keyof T>(key: K): T[K];
18401845

1846+
// Reading deep values
1847+
1848+
hasIn(keyPath: ESIterable<any>): boolean;
1849+
getIn(keyPath: ESIterable<any>): any;
1850+
18411851
// Value equality
18421852

18431853
equals(other: any): boolean;
@@ -1850,25 +1860,27 @@
18501860
merge(...iterables: Array<Partial<T> | ESIterable<[string, any]>>): this;
18511861
mergeDeep(...iterables: Array<Partial<T> | ESIterable<[string, any]>>): this;
18521862

1853-
/**
1854-
* @alias remove
1855-
*/
1856-
delete<K extends keyof T>(key: K): this;
1857-
remove<K extends keyof T>(key: K): this;
1858-
clear(): this;
1863+
mergeWith(
1864+
merger: (oldVal: any, newVal: any, key: keyof T) => any,
1865+
...iterables: Array<Partial<T> | ESIterable<[string, any]>>
1866+
): this;
1867+
mergeDeepWith(
1868+
merger: (oldVal: any, newVal: any, key: any) => any,
1869+
...iterables: Array<Partial<T> | ESIterable<[string, any]>>
1870+
): this;
18591871

18601872
// Deep persistent changes
18611873

1862-
setIn(keyPath: Array<any> | Iterable<any, any>, value: any): this;
1863-
updateIn(keyPath: Array<any> | Iterable<any, any>, updater: (value: any) => any): this;
1864-
mergeIn(keyPath: Array<any> | Iterable<any, any>, ...iterables: Array<any>): this;
1865-
mergeDeepIn(keyPath: Array<any> | Iterable<any, any>, ...iterables: Array<any>): this;
1874+
setIn(keyPath: ESIterable<any>, value: any): this;
1875+
updateIn(keyPath: ESIterable<any>, updater: (value: any) => any): this;
1876+
mergeIn(keyPath: ESIterable<any>, ...iterables: Array<any>): this;
1877+
mergeDeepIn(keyPath: ESIterable<any>, ...iterables: Array<any>): this;
18661878

18671879
/**
18681880
* @alias removeIn
18691881
*/
1870-
deleteIn(keyPath: Array<any> | Iterable<any, any>): this;
1871-
removeIn(keyPath: Array<any> | Iterable<any, any>): this;
1882+
deleteIn(keyPath: ESIterable<any>): this;
1883+
removeIn(keyPath: ESIterable<any>): this;
18721884

18731885
// Conversion to JavaScript types
18741886

@@ -1909,6 +1921,8 @@
19091921

19101922
// Sequence algorithms
19111923

1924+
toSeq(): Seq.Keyed<keyof T, T[keyof T]>;
1925+
19121926
[Symbol.iterator](): Iterator<[keyof T, T[keyof T]]>;
19131927
}
19141928
}

dist/immutable.d.ts

+26-12
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@ declare module Immutable {
191191
*/
192192
export function isOrdered(maybeOrdered: any): boolean;
193193

194+
/**
195+
* True if `maybeRecord` is an instance of a Record.
196+
*/
197+
export function isRecord(maybeRecord: any): maybeRecord is Record.Instance<any>;
198+
194199
/**
195200
* True if `maybeValue` is a JavaScript Object which has *both* `equals()`
196201
* and `hashCode()` methods.
@@ -1838,6 +1843,11 @@ declare module Immutable {
18381843
has(key: string): boolean;
18391844
get<K extends keyof T>(key: K): T[K];
18401845

1846+
// Reading deep values
1847+
1848+
hasIn(keyPath: ESIterable<any>): boolean;
1849+
getIn(keyPath: ESIterable<any>): any;
1850+
18411851
// Value equality
18421852

18431853
equals(other: any): boolean;
@@ -1850,25 +1860,27 @@ declare module Immutable {
18501860
merge(...iterables: Array<Partial<T> | ESIterable<[string, any]>>): this;
18511861
mergeDeep(...iterables: Array<Partial<T> | ESIterable<[string, any]>>): this;
18521862

1853-
/**
1854-
* @alias remove
1855-
*/
1856-
delete<K extends keyof T>(key: K): this;
1857-
remove<K extends keyof T>(key: K): this;
1858-
clear(): this;
1863+
mergeWith(
1864+
merger: (oldVal: any, newVal: any, key: keyof T) => any,
1865+
...iterables: Array<Partial<T> | ESIterable<[string, any]>>
1866+
): this;
1867+
mergeDeepWith(
1868+
merger: (oldVal: any, newVal: any, key: any) => any,
1869+
...iterables: Array<Partial<T> | ESIterable<[string, any]>>
1870+
): this;
18591871

18601872
// Deep persistent changes
18611873

1862-
setIn(keyPath: Array<any> | Iterable<any, any>, value: any): this;
1863-
updateIn(keyPath: Array<any> | Iterable<any, any>, updater: (value: any) => any): this;
1864-
mergeIn(keyPath: Array<any> | Iterable<any, any>, ...iterables: Array<any>): this;
1865-
mergeDeepIn(keyPath: Array<any> | Iterable<any, any>, ...iterables: Array<any>): this;
1874+
setIn(keyPath: ESIterable<any>, value: any): this;
1875+
updateIn(keyPath: ESIterable<any>, updater: (value: any) => any): this;
1876+
mergeIn(keyPath: ESIterable<any>, ...iterables: Array<any>): this;
1877+
mergeDeepIn(keyPath: ESIterable<any>, ...iterables: Array<any>): this;
18661878

18671879
/**
18681880
* @alias removeIn
18691881
*/
1870-
deleteIn(keyPath: Array<any> | Iterable<any, any>): this;
1871-
removeIn(keyPath: Array<any> | Iterable<any, any>): this;
1882+
deleteIn(keyPath: ESIterable<any>): this;
1883+
removeIn(keyPath: ESIterable<any>): this;
18721884

18731885
// Conversion to JavaScript types
18741886

@@ -1909,6 +1921,8 @@ declare module Immutable {
19091921

19101922
// Sequence algorithms
19111923

1924+
toSeq(): Seq.Keyed<keyof T, T[keyof T]>;
1925+
19121926
[Symbol.iterator](): Iterator<[keyof T, T[keyof T]]>;
19131927
}
19141928
}

0 commit comments

Comments
 (0)