[![build status](https://img.shields.io/travis/gcanti/tcomb-react/master.svg?style=flat-square)](https://travis-ci.org/gcanti/tcomb-react) [![dependency status](https://img.shields.io/david/gcanti/tcomb-react.svg?style=flat-square)](https://david-dm.org/gcanti/tcomb-react) ![npm downloads](https://img.shields.io/npm/dm/tcomb-react.svg) # Features - **by default props are required**, a saner default since it's quite easy to forget `.isRequired` - **checks for unwanted additional props** - **documentation** (types and comments) can be automatically extracted - additional fine grained type checks, nestable at arbitrary level - builds on [tcomb](https://github.com/gcanti/tcomb), [tcomb-validation](https://github.com/gcanti/tcomb-validation), [tcomb-doc](https://github.com/gcanti/tcomb-doc) libraries # Compatibility `tcomb-react` has been tested and found working on the following targets. The list is not exhaustive and `tcomb-react` will probably work on other versions that haven't been listed. React: `^0.13.0`, `^0.14.0`, `^15.0.0` # Prop types ## The `@props` decorator (ES7) For an equivalent implementation in ES5, or for Stateless Components, see the `propTypes` function below. **Signature** ```js type Props = {[key: string]: TcombType}; type PropsType = TcombStruct | TcombInterface; type Type = Props | PropsType | Refinement; type Options = { strict?: boolean // default true }; @props(type: Type, options?: Options) ``` where - `type` can be a map `string -> TcombType`, a `tcomb` struct, a `tcomb` interface, a refinement of a `tcomb` struct / interface, a refinement of a `tcomb` interface - `options`: - `strict: boolean` (default `true`) if `true` checks for unwanted additional props **Example** (ES7) ```js import t from 'tcomb' import { props } from 'tcomb-react' const Gender = t.enums.of(['Male', 'Female'], 'Gender') const URL = t.refinement(t.String, (s) => s.startsWith('http'), 'URL') @props({ name: t.String, // a required string surname: t.maybe(t.String), // an optional string age: t.Number, // a required number gender: Gender, // an enum avatar: URL // a refinement }) class Card extends React.Component { render() { return (

{this.props.name}

...
) } } ``` **Unwanted additional props** By default `tcomb-react` checks for unwanted additional props: ```js @props({ name: t.String }) class Person extends React.Component { render() { return (

{this.props.name}

) } } ... ``` **Output** ``` Warning: Failed propType: [tcomb] Invalid additional prop(s): [ "surname" ] supplied to Person. ``` **Note**. You can **opt-out** passing the `option` argument `{ strict: false }`. ## The `propTypes` function **Signature** Same as `@props`. **Stateless Component Example** ```js import { propTypes } from 'tcomb-react' const MyComponentProps = t.interface({ name: t.String, }); const MyComponent = (props) => (
); MyComponent.propTypes = propTypes(MyComponentProps); ``` **ES5 `React.createClass` Example** ```js var t = require('tcomb'); var propTypes = require('tcomb-react').propTypes; var Gender = t.enums.of(['Male', 'Female'], 'Gender'); var URL = t.refinement(t.String, function (s) { return s.startsWith('http'); }, 'URL'); var Card = React.createClass({ propTypes: propTypes({ name: t.String, // a required string surname: t.maybe(t.String), // an optional string age: t.Number, // a required number gender: Gender, // an enum avatar: URL // a refinement }), render: function () { return (

{this.props.name}

...
); } }); ``` ## How it works The `@props` decorator sets `propTypes` on the target component to use a [custom validator function](https://facebook.github.io/react/docs/reusable-components.html#prop-validation) built around tcomb types for each specified prop. For example, the following: ```js const URL = t.refinement(t.String, (s) => s.startsWith('http'), 'URL'); @props({ name: t.String, url: URL, }) class MyComponent extends React.Component { // ... } ``` is roughly equivalent to: ```js const URL = t.refinement(t.String, (s) => s.startsWith('http'), 'URL'); class MyComponent extends React.Component { // ... } MyComponent.propTypes = { name: function(props, propName, componentName) { if (!t.validate(props[propName], t.String).isValid()) { return new Error('...'); } }, url: function(props, propName, componentName) { if (!t.validate(props[propName], URL).isValid()) { return new Error('...'); } }, } ``` ## The babel plugin Using [babel-plugin-tcomb](https://github.com/gcanti/babel-plugin-tcomb) you can express `propTypes` as Flow type annotations: ```js import React from 'react' import ReactDOM from 'react-dom' import type { $Refinement } from 'tcomb' import { props } from 'tcomb-react' type Gender = 'Male' | 'Female'; const isUrl = (s) => s.startsWith('http') type URL = string & $Refinement; type Props = { name: string, surname: ?string, age: number, gender: Gender, avatar: URL }; @props(Props) class Card extends React.Component { render() { return (

{this.props.name}

...
) } } ``` # Extract documentation from your components ## The `parse` function Given a path to a component file returns a JSON / JavaScript blob containing **props types, default values and comments**. **Signature** ```js (path: string | Array) => Object ``` **Example** Source ```js import t from 'tcomb' import { props } from 'tcomb-react' /** * Component description here * @param name - name description here * @param surname - surname description here */ @props({ name: t.String, // a required string surname: t.maybe(t.String) // an optional string }) export default class Card extends React.Component { static defaultProps = { surname: 'Canti' // default value for surname prop } render() { return (

{this.props.name}

{this.props.surname}

) } } ``` Usage ```js import parse from 'tcomb-react/lib/parse' const json = parse('./components/Card.js') console.log(JSON.stringify(json, null, 2)) ``` Output ```json { "name": "Card", "description": "Component description here", "props": { "name": { "kind": "irreducible", "name": "String", "required": true, "description": "name description here" }, "surname": { "kind": "irreducible", "name": "String", "required": false, "defaultValue": "Canti", "description": "surname description here" } } } ``` **Note**. Since `parse` uses runtime type introspection, your components should be `require`able from your script (you may be required to shim the browser environment). **Parsing multiple components** ```js import parse from 'tcomb-react/lib/parse' import path from 'path' import glob from 'glob' function getPath(file) { return path.resolve(process.cwd(), file); } parse(glob.sync('./components/*.js').map(getPath)); ``` ## The `toMarkdown` function Given a JSON / JavaScript blob returned by `parse` returns a markdown containing the components documentation. **Signature** ```js (json: Object) => string ``` **Example** Usage ```js import parse from 'tcomb-react/lib/parse' import toMarkdown from 'tcomb-react/lib/toMarkdown' const json = parse('./components/Card.js') console.log(toMarkdown(json)); ``` Output ```markdown ## Card Component description here **Props** - `name: String` name description here - `surname: String` (optional, default: `"Canti"`) surname description here ``` # Augmented pre-defined types `tcomb-react` exports some useful pre-defined types: - `ReactElement` - `ReactNode` - `ReactChild` - `ReactChildren` **Example** ```js import { props, ReactChild } from 'tcomb-react'; @props({ children: ReactChild // only one child is allowed }) class MyComponent extends React.Component { render() { return (
{this.props.children}
); } } ``` # Support for babel-plugin-tcomb The following types for Flow are exported: - `ReactElementT` - `ReactNodeT` - `ReactChildT` - `ReactChildrenT` # Comparison table | Type | React | tcomb-react | |------|-------|-------------| | array | array | Array | | boolean | bool | Boolean | | functions | func | Function | | numbers | number | Number | | objects | object | Object | | strings | string | String | | all | any | Any | | required prop | T.isRequired | T | | optional prop | T | maybe(T) | | custom types | ✘ | ✓ | | tuples | ✘ | tuple([T, U, ...]) | | lists | arrayOf(T) | list(T) | | instance | instanceOf(A) | T | | dictionaries | objectOf(T) | dict(T, U) (keys are checked) | | enums | oneOf(['a', 'b']) | enums.of('a b') | | unions | oneOfType([T, U]) | union([T, U]) | | duck typing | shape | interface | | react element | element | ReactElement | | react node | node | ReactNode | | react child | ✘ | ReactChild | | react children | ✘ | ReactChildren |