Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursively Create ReadOnly Object in FlowJS

In redux, the state should be immutable. I would like Flow to prevent anyone from mutating that state. So, given an object of arbitrary depth:

type object = {
  a: {
    b: {
      d: string
    }
  },
  c: number
}

How can I create a new type that is recursively readonly, so that I cannot do:

let TestFunction = (param: $RecursiveReadOnly<object>) => {
  param.a.b.d = 'some string'
}

The builtin $ReadOnly utility of Flow will create a type like this, which isn't what is needed, because b & d are still writable:

{
  +a: {
    b: {
      d: string
    }
  },
  +c: number
}

I've been trying to use the $Call & $ObjMap(i), but I can't figure out how to recursively travel an object in Flow. The objective is to have this:

{
  +a: {
    +b: {
      +d: string
    }
  },
  +c: number
}
like image 660
Sam Avatar asked Feb 06 '18 01:02

Sam


Video Answer


1 Answers

Thanks to kalley for his solution. From what I understood, kalley tried to make any object received by a function recursively read only. Since I really only needed known objects as parameters, this works perfectly:

// Type definition that works with arbitrary nested objects/arrays etc.
declare type RecursiveReadOnly<O: Object> = $ReadOnly<$ObjMap<O, typeof makeRecursive>>
declare type RecursiveReadOnlyArray<O: Object> = $ReadOnlyArray<$ReadOnly<$ObjMap<O, typeof makeRecursive>>>
type Recursive<O: Object> = $ObjMap<O, typeof makeRecursive>

declare function makeRecursive<F: Function>(F): F
declare function makeRecursive<A: Object[]>(A): $ReadOnlyArray<$ReadOnly<Recursive<$ElementType<A, number>>>>
declare function makeRecursive<O: Object>(O): RecursiveReadOnly<O>
declare function makeRecursive<I: string[] | boolean[] | number[]>(I): $ReadOnlyArray<$ElementType<I, number>>
declare function makeRecursive<I: string | boolean | number | void | null>(I): I

// Usage example.
type obj = {
  a: {
    b: {
      d: string,
    }
  }
}


let TestFunction = (param: RecursiveReadOnly<obj>) => {
  param.a.b.d = 'some string' // Flow throws an error
}
like image 85
Sam Avatar answered Oct 06 '22 08:10

Sam