Skip to content

Commit 35c2af0

Browse files
committed
Merge branch 'recordTypes' into 4.0
2 parents 625e2ce + 5001072 commit 35c2af0

File tree

8 files changed

+270
-42
lines changed

8 files changed

+270
-42
lines changed

__tests__/RecordJS.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ jest.autoMockOff();
22

33
var Immutable = require('immutable');
44
var Record = Immutable.Record;
5+
var Nullable = Immutable.Nullable;
56

67
describe('Record', () => {
78

@@ -64,4 +65,104 @@ describe('Record', () => {
6465
t2 = t2.clear();
6566
expect(t2.d).toBe(4);
6667
});
68+
69+
it('can have nullable fields', () => {
70+
var A = Record({ x: 0 });
71+
var B = Record({ a: Nullable(A) });
72+
expect(B().toJS()).toEqual({ a: null });
73+
expect(B({}).toJS()).toEqual({ a: null });
74+
expect(B({a:{}}).toJS()).toEqual({ a: { x: 0 } });
75+
expect(B({a:{x: 10}}).toJS()).toEqual({ a: { x: 10 } });
76+
});
77+
78+
it('can have self-referential fields', () => {
79+
var Node = Record(() => ({ next: Nullable(Node) }));
80+
expect(Node().toJS()).toEqual({ next: null });
81+
expect(Node({}).toJS()).toEqual({ next: null });
82+
expect(Node({next:{}}).toJS()).toEqual({ next: { next: null }});
83+
});
84+
85+
it('can describe an infinite type', () => {
86+
var Node = Record(() => ({ next: Node }));
87+
// danger! calling toJS, toString, or reifying this in any way will cause
88+
// a stack overflow!
89+
var node = Node();
90+
expect(node.get('next') instanceof Node).toBe(true);
91+
expect(node.get('next').get('next') instanceof Node).toBe(true);
92+
expect(node.getIn(['next', 'next', 'next', 'next']) instanceof Node).toBe(true);
93+
});
94+
95+
it('can construct sub-records', () => {
96+
var Field = Record({
97+
value: '',
98+
isFocused: false
99+
});
100+
101+
var Login = Record({
102+
user: Field,
103+
password: Field
104+
});
105+
106+
var l1 = Login();
107+
expect(l1.equals(new Login())).toBe(true);
108+
expect(l1.user instanceof Field).toBe(true);
109+
expect(l1.password instanceof Field).toBe(true);
110+
expect(l1.user.value).toBe('');
111+
expect(l1.user.isFocused).toBe(false);
112+
expect(l1.password.value).toBe('');
113+
expect(l1.password.isFocused).toBe(false);
114+
115+
var l2 = Login({ user: { value: 'create' }})
116+
expect(l2.equals(new Login({user: {value: 'create'}}))).toBe(true);
117+
expect(l2.user instanceof Field).toBe(true);
118+
expect(l2.password instanceof Field).toBe(true);
119+
expect(l2.user.value).toBe('create');
120+
expect(l2.user.isFocused).toBe(false);
121+
expect(l2.password.value).toBe('');
122+
expect(l2.password.isFocused).toBe(false);
123+
124+
var l3 = Login({
125+
user: { value: 'create' },
126+
password: { isFocused: true },
127+
extra: { isFocused: false }
128+
});
129+
expect(l3.user instanceof Field).toBe(true);
130+
expect(l3.password instanceof Field).toBe(true);
131+
expect(l3.user.value).toBe('create');
132+
expect(l3.user.isFocused).toBe(false);
133+
expect(l3.password.value).toBe('');
134+
expect(l3.password.isFocused).toBe(true);
135+
expect(l2.extra).toBe(undefined);
136+
});
137+
138+
it('can update sub-records', () => {
139+
var Field = Record({
140+
value: String,
141+
isFocused: Boolean
142+
});
143+
144+
var Login = Record({
145+
user: Field,
146+
password: Field
147+
});
148+
149+
var l1 = Login();
150+
expect(l1.user.value).toBe('');
151+
152+
var l2 = l1.set('user', {value: 'set'})
153+
expect(l2.user instanceof Field).toBe(true);
154+
expect(l2.user.value).toBe('set');
155+
156+
var l3 = l1.updateIn(['user'], () => ({value: 'updateIn'}));
157+
expect(l3.user instanceof Field).toBe(true);
158+
expect(l3.user.value).toBe('updateIn');
159+
160+
var l4 = l2.set('user', null);
161+
expect(l4.user instanceof Field).toBe(true);
162+
expect(l4.user.value).toBe('');
163+
164+
var l5 = l1.merge({user: {value: 'merge'}});
165+
expect(l5.user instanceof Field).toBe(true);
166+
expect(l5.user.value).toBe('merge');
167+
});
67168
});

dist/immutable.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,22 @@ declare module 'immutable' {
10791079
): Record.Class;
10801080

10811081

1082+
/**
1083+
* Provided a factory function, most commonly a Record type, return a new
1084+
* factory function that will return null if provided null.
1085+
*
1086+
* var Point = Record({ x: 0, y: 0 });
1087+
* var NullablePoint = Nullable(Point);
1088+
* Point(); // Record { "x": 0, "y": 0 }
1089+
* Point({}); // Record { "x": 0, "y": 0 }
1090+
* NullablePoint(); // null
1091+
* NullablePoint({}); // Record { "x": 0, "y": 0 }
1092+
*/
1093+
export function Nullable<T, R>(
1094+
TypeFactory: (T) => R
1095+
): (/*?*/T) => /*?*/R;
1096+
1097+
10821098
/**
10831099
* Represents a sequence of values, but may not be backed by a concrete data
10841100
* structure.

dist/immutable.js

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3741,8 +3741,9 @@
37413741

37423742
createClass(Record, KeyedCollection);
37433743

3744-
function Record(defaultValues, name) {
3745-
var hasInitialized;
3744+
function Record(valuesOrTypes, name) {
3745+
var defaultValues;
3746+
var factories;
37463747

37473748
var RecordType = function Record(values) {
37483749
if (values instanceof RecordType) {
@@ -3751,16 +3752,48 @@
37513752
if (!(this instanceof RecordType)) {
37523753
return new RecordType(values);
37533754
}
3754-
if (!hasInitialized) {
3755-
hasInitialized = true;
3756-
var keys = Object.keys(defaultValues);
3755+
if (!defaultValues) {
3756+
defaultValues = {};
3757+
3758+
if (typeof valuesOrTypes === 'function') {
3759+
valuesOrTypes = valuesOrTypes();
3760+
}
3761+
var keys = Object.keys(valuesOrTypes);
3762+
for (var i = 0, l = keys.length; i < l; i++) {
3763+
var valueOrType = valuesOrTypes[keys[i]];
3764+
if (typeof valueOrType === 'function') {
3765+
if (!factories) {
3766+
factories = {};
3767+
}
3768+
factories[keys[i]] = valueOrType;
3769+
} else {
3770+
defaultValues[keys[i]] = valueOrType;
3771+
}
3772+
}
37573773
setProps(RecordTypePrototype, keys);
37583774
RecordTypePrototype.size = keys.length;
37593775
RecordTypePrototype._name = name;
37603776
RecordTypePrototype._keys = keys;
3777+
RecordTypePrototype._factories = factories;
3778+
if (factories) {
3779+
for (i = 0; i < l; i++) {
3780+
var factory = factories[keys[i]];
3781+
defaultValues[keys[i]] = factory();
3782+
}
3783+
}
37613784
RecordTypePrototype._defaultValues = defaultValues;
37623785
}
3763-
this._map = src_Map__Map(values);
3786+
3787+
var map;
3788+
if (factories) {
3789+
map = src_Map__Map(Seq(values).map(function(v, k) {
3790+
var factory = factories[k];
3791+
return factory ? factory(v) : v;
3792+
}));
3793+
} else {
3794+
map = src_Map__Map(values);
3795+
}
3796+
this._map = map;
37643797
};
37653798

37663799
var RecordTypePrototype = RecordType.prototype = Object.create(RecordPrototype);
@@ -3802,7 +3835,9 @@
38023835
if (!this.has(k)) {
38033836
throw new Error('Cannot set unknown key "' + k + '" on ' + recordName(this));
38043837
}
3805-
var newMap = this._map && this._map.set(k, v);
3838+
var factories = this._factories;
3839+
var factory = factories && factories[k];
3840+
var newMap = this._map && this._map.set(k, factory ? factory(v) : v);
38063841
if (this.__ownerID || newMap === this._map) {
38073842
return this;
38083843
}
@@ -4145,6 +4180,12 @@
41454180

41464181
var EMPTY_REPEAT;
41474182

4183+
function Nullable(TypeFactory) {
4184+
return function (values) {
4185+
return values === null || values === undefined ? null : TypeFactory(values);
4186+
}
4187+
}
4188+
41484189
/**
41494190
* Contributes additional methods to a constructor
41504191
*/
@@ -4892,6 +4933,7 @@
48924933
OrderedSet: OrderedSet,
48934934

48944935
Record: Record,
4936+
Nullable: Nullable,
48954937
Range: Range,
48964938
Repeat: Repeat,
48974939

0 commit comments

Comments
 (0)