For example, given the following record:
type UserRecord = {
id: string;
name: ?string;
age: number;
}
Is there some way to do the equivalent of the following:
/* @flow */
import { List, Map } from 'immutable'
const users: List<Map<UserRecord>> = List();
let user: Map<UserRecord>;
user = Map({ id: '666', age: 30 });
users.push(user);
Otherwise I end up simply using something like Map<string, any>
which I think takes away from using Immutable.js with the Flow type system.
In general this is not possible since records and maps have very different semantics. Map type is parameterized with types of of key and value, so when you call .get
you will get the same type for all keys.
There is a way around, though:
declare class Map<T, K1=null, V1=null, K2=null, V2=null> {
constructor(val: T): void;
get(key: K1 & $Keys<T>): V1;
get(key: K2 & $Keys<T>): V2;
// etc
}
const m: Map<{ foo: string, bar: number }, 'foo', string, 'bar', number> = new Map({
'foo': 'foo',
bar: 42
});
m.get('foo') // string
m.get('bar') // number
m.get('baz') // error
It's probably a good idea to generate such declaration with some sort of script to support desired amount of key-value pairs.
Such declaration is a a bit verbose, but safe if you don't mess up type parameters. A couple of comments:
we use recent Flow feature that allows us to declare default type parameter, so that we could use a single declaration for any number of key-value pairs;
K1 & $Keys<T>
ensures that we can only use actual keys of type T
to retrieve values; this helps with consistency to some degree, unfortunately, I found no way to verify consistency of value types, so you have to be careful with those.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With