-
-
Save glenjamin/75a96b45f4bb5c6ac221815d28c548dd to your computer and use it in GitHub Desktop.
/* @flow */ | |
import * as I from "immutable"; | |
/** | |
* Define an immutable record intended for holding reducer state | |
* @param spec - the keys and their default values | |
* @return a state record factory function | |
*/ | |
export function defineRecord<T: Object>( | |
name: string, | |
spec: T | |
): (init: $Shape<T>) => Record<T> { | |
return I.Record(spec, name); | |
} | |
export type Record<T: Object> = RecordMethods<T> & T; | |
declare class RecordMethods<T: Object> { | |
get<A>(key: $Keys<T>): A; | |
set<A>(key: $Keys<T>, value: A): Record<T>; | |
update<A>(key: $Keys<T>, updater: (value: A) => A): Record<T>; | |
updateIn<A>(path: Iterable<any>, notSetOrUpdater: A | (value: A) => A, updater?: (value: A) => A): Record<T>; | |
setIn<A>(path: Iterable<any>, value: A): Record<T>; | |
deleteIn<A>(path: Iterable<any>): Record<T>; | |
merge(values: $Shape<T>): Record<T>; | |
inspect(): string; | |
toObject(): T; | |
// add more as needed | |
} |
We've been able to get record flow types to work using the newest v4.0.0-RC-2 release and a bug fix to the record types. With these fixes we can do the following:
// create new record "class"
const newRecord = Record({ id: 0, name: '' });
// create a dummy instance of that record to use for typing
const dummyInst = newRecord();
// create the type to use when declaring the interface to a component
type recordInterface = typeof dummyInst;
// export the record "class" to be used for record instance creation
export { newRecord };
When a record instance is created and passed around, the type of the record is actually typeof dummyInst
not newRecord
. So in many cases, you create a dummy instance to generate the correct type, even though it may not be used anywhere in the actual javascript.
@ianwcarlson
I've tried to fork Immutable repository and apply mentioned bugfix. It doesn't seem to work.
const Person = Record({
name: null,
age: 0,
isAdult: false,
})
const personInstance = Person()
type TPerson = typeof personInstance
const Animal = Record({
name: null,
owner: null,
})
const animalInstance = Animal()
type TAnimal = typeof animalInstance
export const checkAge = (person: TPerson): void => {
if (person.age >= 18) {
console.log('ADULT')
} else {
console.log('CHILD')
}
}
export const foobar = () => {
const person = Person()
const animal = Animal()
checkAge(animal)
}
In this example, the Flow doesn't detect wrong type of object passed to checkAge
function. It doesn't event detect if I pass a native type there: checkAge(true)
.
Am I doing something wrong?
I'm also not able to get @ianwcarlson fix working. Has anyone been successful ?
@glenjamin in your example, I'm struggling to understand how you use all of the things that are exported:
export type ThingShape
export type ThingRecord
export const Thing
So in that example,
thing
has a type ofThingRecord
, and so I guessThing
is just a function that returns something with a type ofThingRecord
.Ah, I think I get it. But I think it might be clearer if
Thing
was renamed tocreateThingRecord
:Just to clarify that you're not creating a new
Thing
class, you're calling a plain function that returns aThingRecord
.