问题
I would like to describe the shape of a map using Immutable's flow type definitions.
You can describe the shape of an object by:
const stateShape: {
id: number,
isActive: boolean
} = {
id: 123,
isActive: true
};
Is there something similar for Immutable's Maps?
回答1:
TL;DR;
No, but using Records you can get Flow to typecheck the shape but not the types.
Longform
The correct answer would be: no, since maps don't have shapes (at least in Flow and Immutable). But Immutable does have a type for "Maps" with shapes. That would be Records. But for reasons described below (since it's not strictly relevant) the flow libdef for Immutable.Record
is very loose and actually doesn't check for shapes.
A better Record libdef
If we ignore the (arguably unnecessary) feature of directly accessing Record properties, we can create a better libdef. The would look like this:
declare class Record<T: Object> {
static <T: Object>(spec: T, name?: string): Record<T>;
get: <A>(key: $Keys<T>) => A;
set<A>(key: $Keys<T>, value: A): Record<T>;
remove(key: $Keys<T>): Record<T>;
}
With this declaration we can define the shape of the Record. Here it is in action. But we still can't define the types of the actual values. Flow does define an undocumented $PropertyType<T, K>
type. Which takes an object T
and a string literal K
. To make $PropertyType
work in our case it would need to work for $Keys<T>
which is a string union type. A few weeks ago an issue was opened to make this happen. It can be found here.
Difference between Map and Object
In flow they are fairly different. This is a Map:
type MyMaps = { [key: string]: number }
The actual keys are unknown. The only thing Flow knows, is that all keys must be strings and all values must be numbers. An Object type on the other hand looks something like:
type MyObject = { a: string, x: boolean }
When creating or changing an object, newObj
, of type MyObject
Flow, will check that newObj.a
is a string and newObj.x
is a boolean.
Why the current definition is so loose
A Record exposes every key/value pair through direct key access.
type R = { a: string }
const r = Record({ a: 'Supa' })
r.a === r.get('a')
This would require the type definition of r
to be an intersect of Record<R>
and R
(not exactly, but it's close enough). So:
(r: R & Record<R>)
This doesn't work because Flow lacks support for intersect types with objects. Here's how that looks in action.
来源:https://stackoverflow.com/questions/37033339/how-to-describe-immutable-js-map-shape-with-flow