Repository: brigand/babel-plugin-flow-react-proptypes
Pull request #155 (2017-12-16 09:51:52)

simple Generic support
Based on https://github.com/brigand/babel-plugin-flow-react-proptypes/issues/131 I started to work on Generic support.
This enables support for generics that are declared in the same file.
It's not the best but a start!

coveralls: 
[![Coverage Status](https://coveralls.io/builds/14685961/badge)](https://coveralls.io/builds/14685961)

Coverage decreased (-1.0%) to 91.477% when pulling **46b03f91cc0e59b625642b488a2f94a1b8c9dc14 on marudor:master** into **5e77fb119ce396c04c18c3481023908a1f147be3 on brigand:master**.


Patch:
diff --git a/src/convertToPropTypes.js b/src/convertToPropTypes.js
index 83923d5..513f277 100644
--- a/src/convertToPropTypes.js
+++ b/src/convertToPropTypes.js
@@ -7,9 +7,95 @@ function getObjectTypePropertyKey(node) {
   return node.key.name;
 }
 
+function mapGenericToRealType(node, typeParamMapping) {
+  if (node.type === 'GenericTypeAnnotation' && node.id.type === 'Identifier') {
+    return typeParamMapping[node.id.name] || node;
+  }
+  else if (node.type === 'NullableTypeAnnotation') {
+    const realTypeAnnotation = mapGenericToRealType(node.typeAnnotation, typeParamMapping);
+    const newNode = Object.assign({}, node, {
+      typeAnnotation: realTypeAnnotation,
+    });
+    return newNode;
+  }
+  else if (node.type === 'UnionTypeAnnotation') {
+    const realTypes = node.types.map(n => mapGenericToRealType(n, typeParamMapping));
+    const newNode = Object.assign({}, node, {
+      types: realTypes,
+    });
+    return newNode;
+  }
+  else if (node.type === 'ObjectTypeProperty') {
+    const realValue = mapGenericToRealType(node.value, typeParamMapping);
+    const newNode = Object.assign({}, node, {
+      value: realValue,
+    });
+    return newNode;
+  }
+  else if (node.type === 'ObjectTypeAnnotation') {
+    const realProperties = node.properties.map(p => mapGenericToRealType(p, typeParamMapping));
+    const newNode = Object.assign({}, node, {
+      properties: realProperties,
+    });
+    return newNode;
+  }
+  return node;
+}
+
+function convertGenericToPropTypes(node, typeParamMapping, importedTypes, internalTypes) {
+  if (node.type === 'GenericTypeAnnotation' && node.id.type === 'Identifier' && !node.typeParameters) {
+    return convertToPropTypes(typeParamMapping[node.id.name], importedTypes, internalTypes);
+  }
+  else if (node.type === 'GenericTypeAnnotation' && node.typeParameters) {
+    const realTypeParameters = node.typeParameters.params.map(p => mapGenericToRealType(p, typeParamMapping));
+    return internalTypes[node.id.name](realTypeParameters);
+  }
+  else if (node.type === 'ArrayTypeAnnotation') {
+    const realElementType = mapGenericToRealType(node.elementType, typeParamMapping);
+    const realNode = Object.assign({}, node, {
+      elementType: realElementType,
+    });
+    return convertToPropTypes(realNode, importedTypes, internalTypes);
+  }
+  else if (node.type === 'NullableTypeAnnotation') {
+    const result = convertGenericToPropTypes(node.typeAnnotation, typeParamMapping, importedTypes, internalTypes);
+    result.optional = true;
+    return result;
+  }
+  else if (node.type === 'UnionTypeAnnotation') {
+    const types = node.types.map(n => mapGenericToRealType(n, typeParamMapping));
+    const realNode = Object.assign({}, node, {
+      types,
+    });
+    return convertToPropTypes(realNode, importedTypes, internalTypes);
+  }
+  else if (node.type === 'ObjectTypeAnnotation') {
+    const properties = node.properties.map(n => mapGenericToRealType(n, typeParamMapping));
+    const realNode = Object.assign({}, node, {
+      properties,
+    });
+    return convertToPropTypes(realNode, importedTypes, internalTypes);
+  }
+}
+
 export default function convertToPropTypes(node, importedTypes, internalTypes) {
   $debug('convertToPropTypes', node);
   let resultPropType;
+  
+  if (node.type === 'TypeAlias' && node.typeParameters) {
+    return (types) => {
+      const typeParams = node.typeParameters.params.map(t => t.name);
+      const typeParamMapping = {};
+      for (let i = 0; i < typeParams.length; i++) {
+        typeParamMapping[typeParams[i]] = types[i];
+      }
+      // console.log(node.right, typeParamMapping);
+      return convertGenericToPropTypes(node.right, typeParamMapping, importedTypes, internalTypes);
+    };
+  }
+  else if (node.right) {
+    node = node.right;
+  }
 
   if (node.type === 'ObjectTypeAnnotation') {
     const properties = [];
@@ -180,6 +266,9 @@ export default function convertToPropTypes(node, importedTypes, internalTypes) {
       (node.id.id.name === 'Element' || node.id.id.name === 'Node')) {
       resultPropType = {type: 'node'};
     }
+    else if (node.type === 'GenericTypeAnnotation' && node.typeParameters && typeof internalTypes[node.id.name] === 'function') {
+      resultPropType = Object.assign({}, internalTypes[node.id.name](node.typeParameters.params));
+    }
     else if (node.id && node.id.name && internalTypes[node.id.name]) {
       resultPropType = Object.assign({}, internalTypes[node.id.name]);
     }
diff --git a/src/index.js b/src/index.js
index 7e12b66..3570109 100644
--- a/src/index.js
+++ b/src/index.js
@@ -242,14 +242,13 @@ module.exports = function flowReactPropTypes(babel) {
       TypeAlias(path) {
         if (suppress) return;
         $debug('TypeAlias found');
-        const {right} = path.node;
 
         const typeAliasName = path.node.id.name;
         if (!typeAliasName) {
           throw new Error('Did not find name for type alias');
         }
 
-        const propTypes = convertNodeToPropTypes(right);
+        const propTypes = convertNodeToPropTypes(path.node);
         internalTypes[typeAliasName] = propTypes;
       },
       "ClassExpression|ClassDeclaration"(path) {


Test patch:
diff --git a/src/__tests__/__snapshots__/generic-array.js.snap b/src/__tests__/__snapshots__/generic-array.js.snap
new file mode 100644
index 0000000..431ecd5
--- /dev/null
+++ b/src/__tests__/__snapshots__/generic-array.js.snap
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`generic-array 1`] = `
+"'use strict';
+
+Object.defineProperty(exports, \\"__esModule\\", {
+  value: true
+});
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\\"this hasn't been initialised - super() hasn't been called\\"); } return call && (typeof call === \\"object\\" || typeof call === \\"function\\") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== \\"function\\" && superClass !== null) { throw new TypeError(\\"Super expression must either be null or a function, not \\" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var ArrayTest = function (_React$Component) {
+  _inherits(ArrayTest, _React$Component);
+
+  function ArrayTest() {
+    _classCallCheck(this, ArrayTest);
+
+    return _possibleConstructorReturn(this, (ArrayTest.__proto__ || Object.getPrototypeOf(ArrayTest)).apply(this, arguments));
+  }
+
+  return ArrayTest;
+}(_react2.default.Component);
+
+ArrayTest.propTypes = {
+  foo: require('prop-types').arrayOf(require('prop-types').number).isRequired
+};
+exports.default = ArrayTest;"
+`;
diff --git a/src/__tests__/__snapshots__/generic-nested.js.snap b/src/__tests__/__snapshots__/generic-nested.js.snap
new file mode 100644
index 0000000..b11acc3
--- /dev/null
+++ b/src/__tests__/__snapshots__/generic-nested.js.snap
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`generic-nested 1`] = `
+"'use strict';
+
+Object.defineProperty(exports, \\"__esModule\\", {
+  value: true
+});
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\\"this hasn't been initialised - super() hasn't been called\\"); } return call && (typeof call === \\"object\\" || typeof call === \\"function\\") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== \\"function\\" && superClass !== null) { throw new TypeError(\\"Super expression must either be null or a function, not \\" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var ArrayTest = function (_React$Component) {
+  _inherits(ArrayTest, _React$Component);
+
+  function ArrayTest() {
+    _classCallCheck(this, ArrayTest);
+
+    return _possibleConstructorReturn(this, (ArrayTest.__proto__ || Object.getPrototypeOf(ArrayTest)).apply(this, arguments));
+  }
+
+  return ArrayTest;
+}(_react2.default.Component);
+
+ArrayTest.propTypes = {
+  foo: require('prop-types').oneOfType([require('prop-types').oneOfType([typeof A === 'function' ? require('prop-types').instanceOf(A) : require('prop-types').any, require('prop-types').number]), require('prop-types').bool]).isRequired
+};
+exports.default = ArrayTest;"
+`;
diff --git a/src/__tests__/__snapshots__/generic-nullable.js.snap b/src/__tests__/__snapshots__/generic-nullable.js.snap
new file mode 100644
index 0000000..f78f545
--- /dev/null
+++ b/src/__tests__/__snapshots__/generic-nullable.js.snap
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`generic-nullable 1`] = `
+"'use strict';
+
+Object.defineProperty(exports, \\"__esModule\\", {
+  value: true
+});
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\\"this hasn't been initialised - super() hasn't been called\\"); } return call && (typeof call === \\"object\\" || typeof call === \\"function\\") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== \\"function\\" && superClass !== null) { throw new TypeError(\\"Super expression must either be null or a function, not \\" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var ArrayTest = function (_React$Component) {
+  _inherits(ArrayTest, _React$Component);
+
+  function ArrayTest() {
+    _classCallCheck(this, ArrayTest);
+
+    return _possibleConstructorReturn(this, (ArrayTest.__proto__ || Object.getPrototypeOf(ArrayTest)).apply(this, arguments));
+  }
+
+  return ArrayTest;
+}(_react2.default.Component);
+
+ArrayTest.propTypes = {
+  foo: require('prop-types').number
+};
+exports.default = ArrayTest;"
+`;
diff --git a/src/__tests__/__snapshots__/generic-shape.js.snap b/src/__tests__/__snapshots__/generic-shape.js.snap
new file mode 100644
index 0000000..cccd91d
--- /dev/null
+++ b/src/__tests__/__snapshots__/generic-shape.js.snap
@@ -0,0 +1,40 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`generic-shape 1`] = `
+"'use strict';
+
+Object.defineProperty(exports, \\"__esModule\\", {
+  value: true
+});
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\\"this hasn't been initialised - super() hasn't been called\\"); } return call && (typeof call === \\"object\\" || typeof call === \\"function\\") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== \\"function\\" && superClass !== null) { throw new TypeError(\\"Super expression must either be null or a function, not \\" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var ArrayTest = function (_React$Component) {
+  _inherits(ArrayTest, _React$Component);
+
+  function ArrayTest() {
+    _classCallCheck(this, ArrayTest);
+
+    return _possibleConstructorReturn(this, (ArrayTest.__proto__ || Object.getPrototypeOf(ArrayTest)).apply(this, arguments));
+  }
+
+  return ArrayTest;
+}(_react2.default.Component);
+
+ArrayTest.propTypes = {
+  foo: require('prop-types').shape({
+    foo: require('prop-types').number.isRequired
+  }).isRequired
+};
+exports.default = ArrayTest;"
+`;
diff --git a/src/__tests__/__snapshots__/generic-simple.js.snap b/src/__tests__/__snapshots__/generic-simple.js.snap
new file mode 100644
index 0000000..c501268
--- /dev/null
+++ b/src/__tests__/__snapshots__/generic-simple.js.snap
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`generic-simple 1`] = `
+"'use strict';
+
+Object.defineProperty(exports, \\"__esModule\\", {
+  value: true
+});
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\\"this hasn't been initialised - super() hasn't been called\\"); } return call && (typeof call === \\"object\\" || typeof call === \\"function\\") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== \\"function\\" && superClass !== null) { throw new TypeError(\\"Super expression must either be null or a function, not \\" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var ArrayTest = function (_React$Component) {
+  _inherits(ArrayTest, _React$Component);
+
+  function ArrayTest() {
+    _classCallCheck(this, ArrayTest);
+
+    return _possibleConstructorReturn(this, (ArrayTest.__proto__ || Object.getPrototypeOf(ArrayTest)).apply(this, arguments));
+  }
+
+  return ArrayTest;
+}(_react2.default.Component);
+
+ArrayTest.propTypes = {
+  foo: require('prop-types').number.isRequired
+};
+exports.default = ArrayTest;"
+`;
diff --git a/src/__tests__/__snapshots__/nullable-through-generic-test.js.snap b/src/__tests__/__snapshots__/nullable-through-generic-test.js.snap
new file mode 100644
index 0000000..b69d5b4
--- /dev/null
+++ b/src/__tests__/__snapshots__/nullable-through-generic-test.js.snap
@@ -0,0 +1,34 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`nullable 1`] = `
+"'use strict';
+
+Object.defineProperty(exports, \\"__esModule\\", {
+  value: true
+});
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\\"this hasn't been initialised - super() hasn't been called\\"); } return call && (typeof call === \\"object\\" || typeof call === \\"function\\") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== \\"function\\" && superClass !== null) { throw new TypeError(\\"Super expression must either be null or a function, not \\" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var React = require('react');
+
+var Foo = function (_React$Component) {
+  _inherits(Foo, _React$Component);
+
+  function Foo() {
+    _classCallCheck(this, Foo);
+
+    return _possibleConstructorReturn(this, (Foo.__proto__ || Object.getPrototypeOf(Foo)).apply(this, arguments));
+  }
+
+  return Foo;
+}(React.Component);
+
+Foo.propTypes = {
+  foo: typeof T === 'function' ? require('prop-types').instanceOf(T) : require('prop-types').any
+};
+exports.default = Foo;"
+`;
diff --git a/src/__tests__/__snapshots__/union-types-and-contants-through-generic.js.snap b/src/__tests__/__snapshots__/union-types-and-contants-through-generic.js.snap
new file mode 100644
index 0000000..3f55dd2
--- /dev/null
+++ b/src/__tests__/__snapshots__/union-types-and-contants-through-generic.js.snap
@@ -0,0 +1,34 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`union-types-and-constants-through-generic 1`] = `
+"'use strict';
+
+Object.defineProperty(exports, \\"__esModule\\", {
+  value: true
+});
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\\"Cannot call a class as a function\\"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\\"this hasn't been initialised - super() hasn't been called\\"); } return call && (typeof call === \\"object\\" || typeof call === \\"function\\") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== \\"function\\" && superClass !== null) { throw new TypeError(\\"Super expression must either be null or a function, not \\" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var React = require('react');
+
+var Foo = function (_React$Component) {
+  _inherits(Foo, _React$Component);
+
+  function Foo() {
+    _classCallCheck(this, Foo);
+
+    return _possibleConstructorReturn(this, (Foo.__proto__ || Object.getPrototypeOf(Foo)).apply(this, arguments));
+  }
+
+  return Foo;
+}(React.Component);
+
+Foo.propTypes = {
+  id: require('prop-types').oneOfType([require('prop-types').oneOf(['foo']), require('prop-types').oneOf(['bar']), require('prop-types').number]).isRequired
+};
+exports.default = C;"
+`;
diff --git a/src/__tests__/__snapshots__/union-void-through-generic-test.js.snap b/src/__tests__/__snapshots__/union-void-through-generic-test.js.snap
new file mode 100644
index 0000000..b181982
--- /dev/null
+++ b/src/__tests__/__snapshots__/union-void-through-generic-test.js.snap
@@ -0,0 +1,18 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`union-void-through-generic 1`] = `
+"'use strict';
+
+var React = require('react');
+
+function C(props) {
+  return React.createElement(
+    'div',
+    null,
+    props.foo
+  );
+}
+C.propTypes = {
+  foo: require('prop-types').string
+};"
+`;
diff --git a/src/__tests__/generic-array.js b/src/__tests__/generic-array.js
new file mode 100644
index 0000000..069d787
--- /dev/null
+++ b/src/__tests__/generic-array.js
@@ -0,0 +1,22 @@
+const babel = require('babel-core');
+const content = `
+import React from 'react';
+
+type Generic<T> = T[];
+type Props = {
+  foo: Generic<number>,
+};
+
+export default
+class ArrayTest extends React.Component<Props> {
+}
+`;
+
+it('generic-array', () => {
+  const res = babel.transform(content, {
+    babelrc: false,
+    presets: ['es2015', 'stage-1', 'react'],
+    plugins: ['syntax-flow', require('../')],
+  }).code;
+  expect(res).toMatchSnapshot();
+});
diff --git a/src/__tests__/generic-nested.js b/src/__tests__/generic-nested.js
new file mode 100644
index 0000000..138f7fe
--- /dev/null
+++ b/src/__tests__/generic-nested.js
@@ -0,0 +1,23 @@
+const babel = require('babel-core');
+const content = `
+import React from 'react';
+
+type Generic<T> = T | number;
+type Generic2<A> = Generic<A> | bool;
+type Props = {
+  foo: Generic2<string>,
+};
+
+export default
+class ArrayTest extends React.Component<Props> {
+}
+`;
+
+it('generic-nested', () => {
+  const res = babel.transform(content, {
+    babelrc: false,
+    presets: ['es2015', 'stage-1', 'react'],
+    plugins: ['syntax-flow', require('../')],
+  }).code;
+  expect(res).toMatchSnapshot();
+});
diff --git a/src/__tests__/generic-nullable.js b/src/__tests__/generic-nullable.js
new file mode 100644
index 0000000..705380c
--- /dev/null
+++ b/src/__tests__/generic-nullable.js
@@ -0,0 +1,22 @@
+const babel = require('babel-core');
+const content = `
+import React from 'react';
+
+type Generic<T> = ?T;
+type Props = {
+  foo: Generic<number>,
+};
+
+export default
+class ArrayTest extends React.Component<Props> {
+}
+`;
+
+it('generic-nullable', () => {
+  const res = babel.transform(content, {
+    babelrc: false,
+    presets: ['es2015', 'stage-1', 'react'],
+    plugins: ['syntax-flow', require('../')],
+  }).code;
+  expect(res).toMatchSnapshot();
+});
diff --git a/src/__tests__/generic-shape.js b/src/__tests__/generic-shape.js
new file mode 100644
index 0000000..30ccec8
--- /dev/null
+++ b/src/__tests__/generic-shape.js
@@ -0,0 +1,24 @@
+const babel = require('babel-core');
+const content = `
+import React from 'react';
+
+type Generic<T> = {
+  foo: T,
+};
+type Props = {
+  foo: Generic<number>,
+};
+
+export default
+class ArrayTest extends React.Component<Props> {
+}
+`;
+
+it('generic-shape', () => {
+  const res = babel.transform(content, {
+    babelrc: false,
+    presets: ['es2015', 'stage-1', 'react'],
+    plugins: ['syntax-flow', require('../')],
+  }).code;
+  expect(res).toMatchSnapshot();
+});
diff --git a/src/__tests__/generic-simple.js b/src/__tests__/generic-simple.js
new file mode 100644
index 0000000..301fc16
--- /dev/null
+++ b/src/__tests__/generic-simple.js
@@ -0,0 +1,22 @@
+const babel = require('babel-core');
+const content = `
+import React from 'react';
+
+type Generic<T> = T;
+type Props = {
+  foo: Generic<number>,
+};
+
+export default
+class ArrayTest extends React.Component<Props> {
+}
+`;
+
+it('generic-simple', () => {
+  const res = babel.transform(content, {
+    babelrc: false,
+    presets: ['es2015', 'stage-1', 'react'],
+    plugins: ['syntax-flow', require('../')],
+  }).code;
+  expect(res).toMatchSnapshot();
+});
diff --git a/src/__tests__/nullable-through-generic-test.js b/src/__tests__/nullable-through-generic-test.js
new file mode 100644
index 0000000..b9199b2
--- /dev/null
+++ b/src/__tests__/nullable-through-generic-test.js
@@ -0,0 +1,22 @@
+const babel = require('babel-core');
+const content = `
+var React = require('react');
+
+type Generic<T> = ?T
+type FooProps = {
+  foo: Generic<T>,
+}
+
+export default class Foo extends React.Component {
+  props: FooProps
+}
+`;
+
+it('nullable', () => {
+  const res = babel.transform(content, {
+    babelrc: false,
+    presets: ['es2015', 'stage-1', 'react'],
+    plugins: ['syntax-flow', require('../')],
+  }).code;
+  expect(res).toMatchSnapshot();
+});
diff --git a/src/__tests__/union-types-and-contants-through-generic.js b/src/__tests__/union-types-and-contants-through-generic.js
new file mode 100644
index 0000000..c9cda08
--- /dev/null
+++ b/src/__tests__/union-types-and-contants-through-generic.js
@@ -0,0 +1,29 @@
+const babel = require('babel-core');
+const content = `
+var React = require('react');
+
+
+
+type Generic<U> = U | 'bar' | number;
+
+type FooProps = {
+  id: Generic<'foo'>,
+};
+
+
+
+class Foo extends React.Component {
+  props: FooProps;
+}
+
+export default C;
+`;
+
+it('union-types-and-constants-through-generic', () => {
+  const res = babel.transform(content, {
+    babelrc: false,
+    presets: ['es2015', 'stage-1', 'react'],
+    plugins: ['syntax-flow', require('../')],
+  }).code;
+  expect(res).toMatchSnapshot();
+});
diff --git a/src/__tests__/union-void-through-generic-test.js b/src/__tests__/union-void-through-generic-test.js
new file mode 100644
index 0000000..f71f2ec
--- /dev/null
+++ b/src/__tests__/union-void-through-generic-test.js
@@ -0,0 +1,23 @@
+const babel = require('babel-core');
+const content = `
+var React = require('react');
+
+type Maybe<T> = T | void;
+
+type Props = {
+  foo: Maybe<string>,
+};
+
+function C(props: Props) {
+  return <div>{props.foo}</div>;
+}
+`;
+
+it('union-void-through-generic', () => {
+  const res = babel.transform(content, {
+    babelrc: false,
+    presets: ['es2015', 'stage-1', 'react'],
+    plugins: ['syntax-flow', require('../')],
+  }).code;
+  expect(res).toMatchSnapshot();
+});
\ No newline at end of file