flow-runtime icon indicating copy to clipboard operation
flow-runtime copied to clipboard

Type Refinement Function Generation

Open bwestergard opened this issue 7 years ago • 5 comments

This is a:

  • [ ] Bug Report
  • [ ] Feature Request
  • [x] Question
  • [ ] Other

Which concerns:

  • [x] flow-runtime
  • [x] babel-plugin-flow-runtime
  • [ ] flow-runtime-validators
  • [ ] flow-runtime-mobx
  • [ ] flow-config-parser
  • [ ] The documentation website

Consider a program that ought to:

  1. Take a string as input.
  2. Parses this string as JSON.
  3. If JSON conforms to a flow type (User), continue, otherwise throw an exception and halt.
  4. Evaluate some total function over the aforementioned type.

Let's say the type that we'd like to validate against in Step #3 is:

type User = {
name: string,
id: number
}

For step #2 I can write something like:

export type JSON = | string | number | boolean | null | JSONObject | JSONArray
export type JSONObject = { [key:string]: JSON }
export type JSONArray = JSON[]

export const typedJSON = (x: mixed) : JSON => {
  switch (typeof x) {
    case 'object':
      if (x === null) {
        return x
      } else if (Array.isArray(x)) {
        return x.map(typedJSON)
      }
      const o: JSONObject = {}
      for (const k in x) {
        o[k] = typedJSON(x[k])
      }
      return o
    case 'string':
    case 'number':
    case 'boolean':
      return x
    default:
      throw new Error('Invalid JSON')
  }
}

I could also manually write a function with the signature (j: JSON) => User that checks for the presence of the name/id keys and ensures their values are a string and number respectively, throwing an exception otherwise. How can I generate such validation functions from flow definitions with flow-runtime and ensure that flow infers the correct return type?

bwestergard avatar Jun 13 '17 20:06 bwestergard

Is this something you would be amenable to supporting?

bwestergard avatar Jun 23 '17 18:06 bwestergard

@bwestergard maybe I'm missing something, but it seems like you could accomplish this with something like:

function parseUser(input: string): User {
  return JSON.parse(input);
}

Or is the issue that you'd like to do this in production without the rest of flow-runtime's overhead?

phpnode avatar Jun 24 '17 19:06 phpnode

Yes, I'd like to use a library like flow-runtime in production to do runtime validation and be assured by flow that the validation function has the right type. See this comment.

bwestergard avatar Jun 28 '17 16:06 bwestergard

I spent a long time looking into this a couple of months ago, what I wanted (and still do want) is to be able to take a Flow (runtime) type, convert it to JSON Schema and back again. It turned out to be a lot of work to get right, especially as ideally the conversion happens at compile time so that you don't need to include flow-runtime in prod, but that again turns into a huge can of worms. It is possible to fix these things, but I can't see myself having a full week or two to do the work any time in the near future. If you do have the time and the inclination I could give you some pointers on where to start?

phpnode avatar Jun 28 '17 18:06 phpnode

@bwestergard How about this?

import { reify } from 'flow-runtime';
import type { Type } from 'flow-runtime';

const user = (reify: Type<User>).assert(json);

You'll need to apply the babel plugin: https://github.com/codemix/flow-runtime/tree/master/packages/babel-plugin-flow-runtime

dsilvasc avatar Jan 16 '18 23:01 dsilvasc