How to describe Immutable.js Map shape with Flow

我的未来我决定 提交于 2019-11-28 19:07:25

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.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!