diff --git a/type-definitions/Immutable.d.ts b/type-definitions/Immutable.d.ts index 76f13f5db9..2637d761d3 100644 --- a/type-definitions/Immutable.d.ts +++ b/type-definitions/Immutable.d.ts @@ -2415,6 +2415,33 @@ declare module Immutable { * var myRecord = new ABRecord({b: 3}) * myRecord.getAB() // 4 * ``` + * + * + * **Flow Typing Records:** + * + * Immutable.js exports two Flow types designed to make it easier to use + * Records with flow typed code, `RecordOf` and `RecordFactory`. + * + * When defining a new kind of Record factory function, use a flow type that + * describes the values the record contains along with `RecordFactory`. + * To type instances of the Record (which the factory function returns), + * use `RecordOf`. + * + * Typically, new Record definitions will export both the Record factory + * function as well as the Record instance type for use in other code. + * + * ```js + * import type { RecordFactory, RecordOf } from 'immutable'; + * + * // Use RecordFactory for defining new Record factory functions. + * type Point3DFields = { x: number, y: number, z: number }; + * const makePoint3D: RecordFactory = Record({ x: 0, y: 0, z: 0 }); + * export makePoint3D; + * + * // Use RecordOf for defining new instances of that Record. + * export type Point3D = RecordOf; + * const some3DPoint: Point3D = makePoint3D({ x: 10, y: 20, z: 30 }); + * ``` */ export module Record { diff --git a/type-definitions/tests/immutable-flow.js b/type-definitions/tests/immutable-flow.js index 7866aa5ef5..b902b0edef 100644 --- a/type-definitions/tests/immutable-flow.js +++ b/type-definitions/tests/immutable-flow.js @@ -31,6 +31,7 @@ import type { KeyedSeq, IndexedSeq, SetSeq, + RecordFactory, RecordOf, } from '../../' @@ -786,14 +787,14 @@ let maybeNumberSeqSize: ?number = numberSeq.size /* Record */ -type PersonRecordMembers = { age: number, name: string } -const PersonRecordClass = Record(({ +type PersonRecordFields = { age: number, name: string } +type PersonRecord = RecordOf; +const makePersonRecord: RecordFactory = Record({ age: 12, name: 'Facebook', -}: PersonRecordMembers)) -type PersonRecordInstance = RecordOf +}); -const personRecordInstance: PersonRecordInstance = PersonRecordClass({ age: 25 }) +const personRecordInstance: PersonRecord = makePersonRecord({ age: 25 }) // $ExpectError { const age: string = personRecordInstance.get('age') } diff --git a/type-definitions/tests/record.js b/type-definitions/tests/record.js index 212a6720fd..142150f4a9 100644 --- a/type-definitions/tests/record.js +++ b/type-definitions/tests/record.js @@ -76,32 +76,41 @@ var t1a: string = t1.a; var t1c = t1.c; // Use of new to create record factories (supported, but discouraged) -const PointNew = new Record({x:0, y:0}); +type TPointNew = {x: number, y: number}; +type PointNew = RecordOf; +const MakePointNew: RecordFactory = new Record({x:0, y:0}); // Not using new allows returning a record. -const origin: RecordOf<{x:number, y:number}> = PointNew(); -// Can use the Record constructor type as an alternative, -// it just doesn't support property access. -const originAlt1: PointNew = PointNew(); -// Can also sort of use the inner Record values type as an alternative, -// however it does not have the immutable record API, though useful for flowing -// immutable Records where plain objects are expected. -const originAlt2: {x: number, y: number} = PointNew(); +const origin: PointNew = MakePointNew(); // Both get and prop access are supported with RecordOf { const x: number = origin.get('x') } { const x: number = origin.x } // $ExpectError number is not a string { const x: string = origin.x } +// Can use the Record constructor type as an alternative, +// it just doesn't support property access. +const originAlt1: MakePointNew = MakePointNew(); +// Both get and prop access are supported with RecordOf +{ const x: number = originAlt1.get('x') } +// $ExpectError cannot use property access for this alternative annotation +{ const x: number = originAlt1.x } +// Can also sort of use the inner Record values type as an alternative, +// however it does not have the immutable record API, though useful for flowing +// immutable Records where plain objects are expected. +const originAlt2: TPointNew = MakePointNew(); +// $ExpectError cannot use Record API for this alternative annotation +{ const x: number = originAlt2.get('x') } +{ const x: number = originAlt2.x } // $ExpectError Use of new may only return a class instance, not a record -const mistakeOriginNew: RecordOf<{x: number, y: number}> = new PointNew(); +const mistakeOriginNew: PointNew = new MakePointNew(); // An alternative type strategy is instance based -const originNew: PointNew = new PointNew(); +const originNew: MakePointNew = new MakePointNew(); // Only get, but not prop access are supported with class instances { const x: number = originNew.get('x') } // $ExpectError property `x`. Property not found in RecordInstance { const x: number = originNew.x } // $ExpectError instantiated with invalid type -const mistakeNewRecord = PointNew({x: 'string'}); +const mistakeNewRecord = MakePointNew({x: 'string'}); // $ExpectError instantiated with invalid type -const mistakeNewInstance = new PointNew({x: 'string'}); +const mistakeNewInstance = new MakePointNew({x: 'string'});