Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to wrap a flow type in an immutable container?

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.

like image 865
gf3 Avatar asked Jun 21 '16 18:06

gf3


1 Answers

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.

like image 119
vkurchatkin Avatar answered Sep 23 '22 09:09

vkurchatkin