1.0.6 • Published 7 years ago

flow-type-transformer v1.0.6

Weekly downloads
3
License
MIT
Repository
github
Last release
7 years ago

Flow-Type Transformer

Transforming types in Flow can quickly become a bit cumbersome to do properly. This package is an extremely tiny (+-500 bytes) value transformer which attempts to simplify transforming values while maintaining 100% flow coverage.

It exports some basic flow utility-types that can then be used to compose your transformation.

Note: When using the package in development, flow types from the package are retained.

Why?

The actual functionality of this package is extremely simple and generally not very difficult to implement outside of the package. However, we have found that the implementation can often cause hiccups and coverage annoyances.

With flow-type-transformer we make the transformation of values feel like a first-class feature of Flow. It provides a simple utility at runtime and a much more useful utility during development (stronger typing and coverage).

Installation

yarn add flow-type-transformer

or

npm install --save flow-type-transformer

Overview

flow-type-transformer exports a factory function which returns an instance of TypeTransformer which can then be used to transform your values while maintaining type checking.

A transform is really just a simple proxy to some value transformation function which makes it easier to interface with flow in a way that is standard. We hope to include pre-built transforms that are able to take in common values and transform them at some point (likely as a separate package).

In reality, the transformer is actually returning your transform function directly. The instance is used simply to annotate the transformation in a way that flow can understand.

For now, take a look at the transforms folder and the tests/flow folder for examples.

Note: If you have useful transform functions that you'd be ok with including in our transforms package, pull requests are more than welcome for those!


Runtime Exports

The runtime export should not add any noticeable performance hit to your application. It ends up following the simple pattern of createTransformer: tranformer => transformer during runtime. A microsecond or two during initialization and 0 after that.

import createTransformer from 'flow-type-transformer'

createTypeTransformer(fn) (default)

The default export of the package. Simply takes a $Transformer function and returns the same function. Our magic happens because in the middle we are able to teach Flow the expected input/output in a way that is both functional and re-useable.

function createTypeTransformer<Before, After>(
  fn: $Transformer<Before, After>,
): $Transform<Before, After>

If you prefer, createTypeTransformer is also exported as a named module which can be imported via import { createTypeTransformer } from 'flow-type-transformer'


Type / Flow Exports

This package exports various Flow types that can be used in your project. These will be stripped from the project completely when you strip the flow-types.

They can be imported using the syntax:

import type { $Transform, $Transformer } from 'flow-type-transformer'

interface $Transform (Interface)

The $Transform interface is how we will define our type transformation when we call our factory function. It takes two parameters: <Before, After> where Before is the type that will be given to the transform function and After will be the resulting output.

interface $Transform<Before, After> {
  transformer: $Transformer<Before, After>,
  $call(v: Before): After,
}

In reality, your transformer function is directly returned and the interface is used simply to help Flow understand your transformation.

type $Transformer (Function)

The type used to define our transform function. It simply take a value of type Before and returns a value of type After. Your transformer function must follow this signature or Flow will complain.

type $Transformer<Before, After> = (value: Before) => After;
const stringToNumberTransformer: $Transformer<string, number> = str => Number(str)

Note that you should not generally not need to import or use this type at all. Since we are using it internally, Flow will inform you if your transformer does not appear to match the expected signature.


Examples

Below are some simplified examples. In general all examples will maintain 100% flow coverage. Some that are shown here may be simplified. For more examples be sure to browse through the flow examples


Create a Transformer

flow-type-transformer default exports a factory function which returns a class implementing the $Transform interface. It expects a transformer function which takes your input type as input and outputs your output type.

/* @flow */
import type { $Transform } from 'flow-type-transformer';
import createTransformer   from 'flow-type-transformer';

// pointless transformer example (since Flow can infer this)
const stringToNumber: $Transform<string, number> = createTransformer(str => Number(str))
const n = stringToNumber('1')

Simple Example

Below is an extremely simple example which Flow actually implements a solution for natively using the $NonMaybeType, but it is a nice way to illustrate what is going on.

For the sake of simplicity the example below is not providing type annotations on the transform function itself.

Note: The example with 100% flow coverage as well as type-casting examples to show the benefits of strongly typed projects can be found in the array-filter-simple flow-test source.

Note: The transformer will cast the After value in its response - it is up to the transform function to make sure it is performing the necessary transformation. Standard Flow type handling will ensure this is done properly.

/* @flow */
import type { $Transform } from 'flow-type-transformer';
import createTransformer   from 'flow-type-transformer';

// $Transformer<Array<mixed>, Array<string>>
const transformer = v => v.filter(el => typeof el === 'string')

const transform: $Transform<Array<?string>, Array<string>> = createTransformer(transformer);

const before = ['one', undefined, 'two', null];
const after  = transform(before);

(before: Array<string>); // $ExpectError
(after:  Array<string>); // $Works

Since Flow now knows the signature of the returned instance, it can provide us with all of it's Flow goodness:

/*
  $ExpectError
    array literal
    This type is incompatible with the expected param type of
    array type
      ---
      Type argument `T` is incompatible:
      number
      ---
      This type is incompatible with
      string
      ---
*/
transform([1]);

Definitely stroll through the flow examples as they provide some more advanced examples and include error traces, etc. to give a better idea of what is going on.


Contributing

While I can't imagine there is much need to modify the source (let me know if you notice something), always appreciate pull requests to help provide more examples and transforms for others to use!

Just submit a pull request!