是否可以将流类型包装在不可变容器中?

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

例如,给定以下记录:

type UserRecord = {
  id: string;
  name: ?string;
  age: number;
}

是否有某种方法可以执行以下操作:

/* @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);

否则我最终会简单地使用像 Map<string, any> 这样的东西,我认为它会从使用 Immutable.js 和 Flow 类型系统中拿走。

通常这是不可能的,因为记录和地图具有非常不同的语义。映射类型使用键和值的类型进行参数化,因此当您调用 .get 时,您将获得所有键的相同类型。

不过还是有办法的:

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

使用某种脚本生成此类声明以支持所需数量的键值对可能是个好主意。

这样的声明有点冗长,但是如果你不弄乱类型参数的话是安全的。一些评论:

  • 我们使用最新的 Flow 特性,允许我们声明默认类型参数,这样我们就可以对任意数量的键值对使用单个声明;

  • K1 & $Keys<T> 确保我们只能使用 T 类型的实际键来检索值;这在某种程度上有助于一致性,不幸的是,我发现没有办法验证值类型的一致性,所以你必须小心那些。