How to describe Immutable.js Map shape with Flow

后端 未结 1 1263
不思量自难忘°
不思量自难忘° 2020-12-14 00:43

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: {         


        
相关标签:
1条回答
  • 2020-12-14 01:15

    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.

    0 讨论(0)
提交回复
热议问题