Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
217 views
in Technique[技术] by (71.8m points)

How to describe Immutable.js Map shape with Flow

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?

question from:https://stackoverflow.com/questions/37033339/how-to-describe-immutable-js-map-shape-with-flow

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...