Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d23746f

Browse filesBrowse files
authored
Improved Flow support for Record subclasses (#1414)
* Improved Flow support for Record subclasses Not ideal, but this adds dramatically better support for subclassing Records when using Flow. The cost of this is that the default values passed to Record are no longer checked - they should be separately checked :/. I've updated the documentation to show this in the example, as well as including an example of subclassing a Record when using Flow. Fixes #1388 * Update record.js
1 parent 97a33d2 commit d23746f
Copy full SHA for d23746f

File tree

3 files changed

+61
-6
lines changed
Filter options

3 files changed

+61
-6
lines changed

‎type-definitions/Immutable.d.ts

Copy file name to clipboardExpand all lines: type-definitions/Immutable.d.ts
+28-3Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,12 +2239,12 @@ declare module Immutable {
22392239
* **Flow Typing Records:**
22402240
*
22412241
* Immutable.js exports two Flow types designed to make it easier to use
2242-
* Records with flow typed code, `RecordOf<T>` and `RecordFactory<TProps>`.
2242+
* Records with flow typed code, `RecordOf<TProps>` and `RecordFactory<TProps>`.
22432243
*
22442244
* When defining a new kind of Record factory function, use a flow type that
22452245
* describes the values the record contains along with `RecordFactory<TProps>`.
22462246
* To type instances of the Record (which the factory function returns),
2247-
* use `RecordOf<T>`.
2247+
* use `RecordOf<TProps>`.
22482248
*
22492249
* Typically, new Record definitions will export both the Record factory
22502250
* function as well as the Record instance type for use in other code.
@@ -2254,14 +2254,39 @@ declare module Immutable {
22542254
*
22552255
* // Use RecordFactory<TProps> for defining new Record factory functions.
22562256
* type Point3DProps = { x: number, y: number, z: number };
2257-
* const makePoint3D: RecordFactory<Point3DProps> = Record({ x: 0, y: 0, z: 0 });
2257+
* const defaultValues: Point3DProps = { x: 0, y: 0, z: 0 };
2258+
* const makePoint3D: RecordFactory<Point3DProps> = Record(defaultValues);
22582259
* export makePoint3D;
22592260
*
22602261
* // Use RecordOf<T> for defining new instances of that Record.
22612262
* export type Point3D = RecordOf<Point3DProps>;
22622263
* const some3DPoint: Point3D = makePoint3D({ x: 10, y: 20, z: 30 });
22632264
* ```
22642265
*
2266+
* **Flow Typing Record Subclasses:**
2267+
*
2268+
* Records can be subclassed as a means to add additional methods to Record
2269+
* instances. This is generally discouraged in favor of a more functional API,
2270+
* since Subclasses have some minor overhead. However the ability to create
2271+
* a rich API on Record types can be quite valuable.
2272+
*
2273+
* When using Flow to type Subclasses, do not use `RecordFactory<TProps>`,
2274+
* instead apply the props type when subclassing:
2275+
*
2276+
* ```js
2277+
* type PersonProps = {name: string, age: number};
2278+
* const defaultValues: PersonProps = {name: 'Aristotle', age: 2400};
2279+
* const PersonRecord = Record(defaultValues);
2280+
* class Person extends PersonRecord<PersonProps> {
2281+
* getName(): string {
2282+
* return this.get('name')
2283+
* }
2284+
*
2285+
* setName(name: string): this {
2286+
* return this.set('name', name);
2287+
* }
2288+
* }
2289+
* ```
22652290
*
22662291
* **Choosing Records vs plain JavaScript objects**
22672292
*

‎type-definitions/immutable.js.flow

Copy file name to clipboardExpand all lines: type-definitions/immutable.js.flow
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,8 +1384,8 @@ type RecordValues<R> = _RecordValues<*, R>;
13841384

13851385
declare function isRecord(maybeRecord: any): boolean %checks(maybeRecord instanceof RecordInstance);
13861386
declare class Record {
1387-
static <Values: Object>(spec: Values, name?: string): RecordFactory<Values>;
1388-
constructor<Values: Object>(spec: Values, name?: string): RecordFactory<Values>;
1387+
static <Values: Object>(spec: Values, name?: string): typeof RecordInstance;
1388+
constructor<Values: Object>(spec: Values, name?: string): typeof RecordInstance;
13891389

13901390
static isRecord: typeof isRecord;
13911391

‎type-definitions/tests/record.js

Copy file name to clipboardExpand all lines: type-definitions/tests/record.js
+31-1Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ const Point3: RecordFactory<{x: number, y: number, z: number}> =
1919
type TGeoPoint = {lat: ?number, lon: ?number}
2020
const GeoPoint: RecordFactory<TGeoPoint> = Record({lat: null, lon: null});
2121

22-
// $ExpectError - 'abc' is not a number
22+
// TODO: this should be ExpectError - 'abc' is not a number
23+
// However, due to support for the brittle support for subclassing, Flow
24+
// cannot also type check default values in this position.
2325
const PointWhoops: RecordFactory<{x: number, y: number}> = Record({x:0, y:'abc'});
2426

2527
let origin2 = Point2({});
@@ -114,3 +116,31 @@ const originNew: MakePointNew = new MakePointNew();
114116
const mistakeNewRecord = MakePointNew({x: 'string'});
115117
// $ExpectError instantiated with invalid type
116118
const mistakeNewInstance = new MakePointNew({x: 'string'});
119+
120+
// Subclassing
121+
122+
type TPerson = {name: string, age: number};
123+
const defaultValues: TPerson = {name: 'Aristotle', age: 2400};
124+
const PersonRecord = Record(defaultValues);
125+
126+
class Person extends PersonRecord<TPerson> {
127+
getName(): string {
128+
return this.get('name');
129+
}
130+
131+
setName(name: string): this & TPerson {
132+
return this.set('name', name);
133+
}
134+
}
135+
136+
const person = new Person();
137+
(person.setName('Thales'): Person);
138+
(person.getName(): string);
139+
(person.setName('Thales').getName(): string);
140+
(person.setName('Thales').name: string);
141+
person.get('name');
142+
person.set('name', 'Thales');
143+
// $ExpectError
144+
person.get('unknown');
145+
// $ExpectError
146+
person.set('unknown', 'Thales');

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.