{"id":13548437,"url":"https://github.com/gcanti/babel-plugin-tcomb","last_synced_at":"2025-10-07T02:31:44.289Z","repository":{"id":32928891,"uuid":"36524685","full_name":"gcanti/babel-plugin-tcomb","owner":"gcanti","description":"Babel plugin for static and runtime type checking using Flow and tcomb","archived":true,"fork":false,"pushed_at":"2019-11-04T07:15:04.000Z","size":456,"stargazers_count":482,"open_issues_count":30,"forks_count":22,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-01-18T10:10:01.788Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gcanti.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-05-29T19:28:27.000Z","updated_at":"2024-07-16T15:29:48.000Z","dependencies_parsed_at":"2022-07-11T09:00:21.298Z","dependency_job_id":null,"html_url":"https://github.com/gcanti/babel-plugin-tcomb","commit_stats":null,"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gcanti%2Fbabel-plugin-tcomb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gcanti%2Fbabel-plugin-tcomb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gcanti%2Fbabel-plugin-tcomb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gcanti%2Fbabel-plugin-tcomb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gcanti","download_url":"https://codeload.github.com/gcanti/babel-plugin-tcomb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235581533,"owners_count":19013089,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T12:01:10.350Z","updated_at":"2025-10-07T02:31:38.948Z","avatar_url":"https://github.com/gcanti.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Plugins"],"sub_categories":["Types"],"readme":"Babel plugin for static and runtime type checking using Flow and tcomb.\n\n**Tools**\n\n[Flow](https://flowtype.org/) is a static type checker for JavaScript.\n\n[tcomb](https://github.com/gcanti/tcomb) is a library for Node.js and the browser which allows you to check the types of JavaScript values at runtime with a simple and concise syntax. It's great for Domain Driven Design and for adding safety to your internal code.\n\n# Why?\n\n**Runtime type checking (tcomb)**\n\n- you don't want or you can't use `Flow`\n- you want refinement types\n- you want to validate the IO boundary (for example API payloads)\n- you want to enforce immutability\n- you want to leverage the runtime type introspection provided by `tcomb`'s types\n\n**Static type checking (Flow)**\n\n`babel-plugin-tcomb` is `Flow` compatible, this means that you can run them side by side, statically checking your code with `Flow` and let `tcomb` catching the remaining bugs at runtime.\n\n# Gentle migration path\n\nYou can add type safety to your untyped codebase gradually:\n\n- first, add type annotations where you think they are most useful, file by file, leveraging the runtime type safety provided by `tcomb`\n- then, when you feel comfortable, turn on `Flow` and unleash the power of static type checking\n- third, for even more type safety, define your refinement types and validate the IO boundary\n\n# Fork\n\n[Here](https://github.com/christophehurpeau/babel-plugin-tcomb) you can find a fork of this plugin that provides the following additional features:\n\n- Avoid checks on confident assignment\n- Bounded polymorphism partial support\n- `let` checks\n- Assignment type checking\n\n# Setup\n\nFirst, install via npm.\n\n```sh\nnpm install --save-dev tcomb\nnpm install --save-dev babel-plugin-tcomb\n```\n\nThen, in your babel configuration (usually in your `.babelrc` file), add (at least) the following plugins:\n\n```js\n{\n  \"plugins\" : [\n    \"syntax-flow\",\n    \"tcomb\",\n    \"transform-flow-strip-types\"\n  ]\n}\n```\n\n**Note**. ``syntax-flow`` and ``transform-flow-strip-types`` are already included with the [React Preset](https://babeljs.io/docs/plugins/preset-react/).\n\n**Note**. Use [Babel's env option](https://babeljs.io/docs/usage/babelrc/) to only use this plugin in development.\n\n**Warning**. If you use multiple presets and are experiencing issues, try tweaking the preset order and setting ``passPerPreset: true``.  Related issues: [#78](https://github.com/gcanti/babel-plugin-tcomb/issues/78) [#99](https://github.com/gcanti/babel-plugin-tcomb/issues/99)\n\n**Important**. `tcomb` must be `require`able\n\n# Plugin configuration\n\n## `skipAsserts?: boolean = false`\n\nRemoves the asserts and keeps the domain models\n\n## `warnOnFailure?: boolean = false`\n\nWarns (`console.warn`) about type mismatch instead of throwing an error\n\n## `globals?: Array\u003cObject\u003e`\n\nWith this option you can handle global types, like `Class` or react `SyntheticEvent`\n\nExample\n\n```js\n\"plugins\" : [\n  [\"tcomb\", {\n    globals: [\n      // flow\n      {\n        'Class': true\n      }\n      // react\n      {\n        'SyntheticEvent': true,\n        ...\n      },\n      // your custom global types (if any)\n      ...\n    ]\n  }],\n]\n```\n\n# Definition files\n\nDefinition files for `tcomb` and `tcomb-react` are temporarily published [here](https://github.com/gcanti/pantarei).\n\n# Caveats\n\n- `tcomb` must be `require`able\n- type parameters (aka generics) are not handled (`Flow`'s responsibility)\n\n# How it works\n\nFirst, add type annotations.\n\n```js\n// index.js\n\nfunction sum(a: number, b: number) {\n  return a + b\n}\n\nsum(1, 'a') // \u003c= typo\n```\n\nThen run `Flow` (static type checking):\n\n```\nindex.js:7\n  7: sum(1, 'a')\n     ^^^^^^^^^^^ function call\n  7: sum(1, 'a')\n            ^^^ string. This type is incompatible with\n  3: function sum(a: number, b: number) {\n                                ^^^^^^ number\n```\n\nor refresh your browser and look at the console (runtime type checking):\n\n```\nUncaught TypeError: [tcomb] Invalid value \"a\" supplied to b: Number\n```\n\n## Domain models\n\n```js\n// index.js\n\ntype Person = {\n  name: string, // required string\n  surname?: string, // optional string\n  age: number,\n  tags: Array\u003cstring\u003e\n};\n\nfunction getFullName(person: Person) {\n  return `${person.name} ${person.surname}`\n}\n\ngetFullName({ surname: 'Canti' })\n```\n\n`Flow`:\n\n```\nindex.js:14\n 14: getFullName({\n     ^ function call\n 10: function getFullName(person: Person) {\n                                  ^^^^^^ property `name`. Property not found in\n 14: getFullName({\n                 ^ object literal\n```\n\n`tcomb`:\n\n```\nTypeError: [tcomb] Invalid value undefined supplied to person: Person/name: String\n```\n\n## Refinements\n\nIn order to define [refinement types](https://github.com/gcanti/tcomb/blob/master/docs/API.md#the-refinement-combinator) you can use the `$Refinement` type, providing a predicate identifier:\n\n```js\nimport type { $Refinement } from 'tcomb'\n\n// define your predicate...\nconst isInteger = n =\u003e n % 1 === 0\n\n// ...and pass it to the suitable intersection type\ntype Integer = number \u0026 $Refinement\u003ctypeof isInteger\u003e;\n\nfunction foo(n: Integer) {\n  return n\n}\n\nfoo(2)   // flow ok, tcomb ok\nfoo(2.1) // flow ok, tcomb throws [tcomb] Invalid value 2.1 supplied to n: Integer\nfoo('a') // flow throws, tcomb throws\n```\n\nIn order to enable this feature add the [`tcomb` definition file](https://github.com/gcanti/pantarei/blob/master/tcomb/3.x.x-0.33.x/tcomb.js) to the `[libs]` section of your `.flowconfig`.\n\n## Runtime type introspection\n\nCheck out the [meta object](https://github.com/gcanti/tcomb/blob/master/docs/API.md#the-meta-object) in the tcomb documentation.\n\n```js\nimport type { $Reify } from 'tcomb'\n\ntype Person = { name: string };\n\nconst ReifiedPerson = (({}: any): $Reify\u003cPerson\u003e)\nconsole.log(ReifiedPerson.meta) // =\u003e { kind: 'interface', props: ... }\n```\n\nIn order to enable this feature add the [`tcomb` definition file](https://github.com/gcanti/pantarei/blob/master/tcomb/3.2.2%2B.js) to the `[libs]` section of your `.flowconfig`.\n\n## Validating (at runtime) the IO boundary using typecasts\n\n```js\ntype User = { name: string };\n\nexport function loadUser(userId: string): Promise\u003cUser\u003e {\n  return axios.get('...').then(p =\u003e (p: User)) // \u003c= type cast\n}\n```\n\n## Recursive types\n\nJust add a `// recursive` comment on top:\n\n```js\n// recursive\ntype Path = {\n  node: Node,\n  parentPath: Path\n};\n```\n\n# Type-checking Redux\n\n```js\nimport { createStore } from 'redux'\n\n// types\ntype State = number;\ntype ReduxInitAction = { type: '@@redux/INIT' };\ntype Action = ReduxInitAction\n  | { type: 'INCREMENT', delta: number }\n  | { type: 'DECREMENT', delta: number };\n\nfunction reducer(state: State = 0, action: Action): State {\n  switch(action.type) {\n    case 'INCREMENT' :\n      return state + action.delta\n    case 'DECREMENT' :\n      return state - action.delta\n  }\n  return state\n}\n\ntype Store = {\n  dispatch: (action: Action) =\u003e any;\n};\n\nconst store: Store = createStore(reducer)\n\nstore.dispatch({ type: 'INCREMEN', delta: 1 }) // \u003c= typo\n\n// throws [tcomb] Invalid value { \"type\": \"INCREMEN\", \"delta\": 1 } supplied to action: Action\n// Flow throws as well\n```\n\n# Type-checking React using tcomb-react\n\nSee [tcomb-react](https://github.com/gcanti/tcomb-react):\n\n```js\n// @flow\n\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { props } from 'tcomb-react'\n\ntype Props = {\n  name: string\n};\n\n@props(Props)\nclass Hello extends React.Component\u003cvoid, Props, void\u003e {\n  render() {\n    return \u003cdiv\u003eHello {this.props.name}\u003c/div\u003e\n  }\n}\n\n\nReactDOM.render(\u003cHello /\u003e, document.getElementById('app'))\n```\n\n`Flow` will throw:\n\n```\nindex.js:12\n 12: class Hello extends React.Component\u003cvoid, Props, void\u003e {\n                                               ^^^^^ property `name`. Property not found in\n 19: ReactDOM.render(\u003cHello /\u003e, document.getElementById('app'))\n                     ^^^^^^^^^ props of React element `Hello`\n```\n\nwhile `tcomb-react` will warn:\n\n```\nWarning: Failed propType: [tcomb] Invalid prop \"name\" supplied to Hello, should be a String.\n\nDetected errors (1):\n\n  1. Invalid value undefined supplied to String\n```\n\nAdditional babel configuration:\n\n```js\n{\n  \"presets\": [\"react\", \"es2015\"],\n  \"passPerPreset\": true,\n  \"plugins\" : [\n    \"tcomb\",\n    \"transform-decorators-legacy\"\n  ]\n}\n```\n\nIn order to enable this feature add the [`tcomb-react` definition file](https://github.com/gcanti/pantarei/blob/master/tcomb-react/0.9.1%2B.js) to the `[libs]` section of your `.flowconfig`.\nAlso you may want to set `esproposal.decorators=ignore` in the `[options]` section of your `.flowconfig`.\n\n### Without decorators\n\n```js\n// @flow\n\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { propTypes } from 'tcomb-react'\nimport type { $Reify } from 'tcomb'\n\ntype Props = {\n  name: string\n};\n\nclass Hello extends React.Component\u003cvoid, Props, void\u003e {\n  render() {\n    return \u003cdiv\u003eHello {this.props.name}\u003c/div\u003e\n  }\n}\n\nHello.propTypes = propTypes((({}: any): $Reify\u003cProps\u003e))\n\nReactDOM.render(\u003cHello /\u003e, document.getElementById('app'))\n```\n\n# Under the hood\n\n## Primitives\n\n```js\ntype MyString = string;\ntype MyNumber = number;\ntype MyBoolean = boolean;\ntype MyVoid = void;\ntype MyNull = null;\n```\n\ncompiles to\n\n```js\nimport _t from \"tcomb\";\n\nconst MyString = _t.String;\nconst MyNumber = _t.Number;\nconst MyBoolean = _t.Boolean;\nconst MyVoid = _t.Nil;\nconst MyNull = _t.Nil;\n```\n\n## Consts\n\n```js\nconst x: number = 1\n```\n\ncompiles to\n\n```js\nconst x = _assert(x, _t.Number, \"x\");\n```\n\nNote: `let`s are not supported.\n\n## Functions\n\n```js\nfunction sum(a: number, b: number): number {\n  return a + b\n}\n```\n\ncompiles to\n\n```js\nimport _t from \"tcomb\";\n\nfunction sum(a, b) {\n  _assert(a, _t.Number, \"a\");\n  _assert(b, _t.Number, \"b\");\n\n  const ret = function (a, b) {\n    return a + b;\n  }.call(this, a, b);\n\n  _assert(ret, _t.Number, \"return value\");\n  return ret;\n}\n```\n\nwhere `_assert` is an helper function injected by `babel-plugin-tcomb`.\n\n## Type aliases\n\n```js\ntype Person = {\n  name: string,\n  surname: ?string,\n  age: number,\n  tags: Array\u003cstring\u003e\n};\n```\n\ncompiles to\n\n```js\nimport _t from \"tcomb\";\n\nconst Person = _t.interface({\n  name: _t.String,\n  surname: _t.maybe(_t.String),\n  age: _t.Number,\n  tags: _t.list(_t.String)\n}, \"Person\");\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgcanti%2Fbabel-plugin-tcomb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgcanti%2Fbabel-plugin-tcomb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgcanti%2Fbabel-plugin-tcomb/lists"}