{"id":25793769,"url":"https://github.com/biggyspender/ts-functional-pipe","last_synced_at":"2025-07-10T20:11:21.747Z","repository":{"id":34482985,"uuid":"179563855","full_name":"biggyspender/ts-functional-pipe","owner":"biggyspender","description":"Heavily overloaded functions (pipe/compose) for type-safe function composition in TypeScript","archived":false,"fork":false,"pushed_at":"2023-02-27T19:50:24.000Z","size":2762,"stargazers_count":25,"open_issues_count":9,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-30T01:02:45.916Z","etag":null,"topics":["compose","composed-functions","function-composition","javascript","pipe","point-free","type-safe","typescript"],"latest_commit_sha":null,"homepage":"https://biggyspender.github.io/ts-functional-pipe","language":"TypeScript","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/biggyspender.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"code-of-conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-04T19:29:44.000Z","updated_at":"2024-05-13T13:57:47.000Z","dependencies_parsed_at":"2024-06-18T20:14:17.774Z","dependency_job_id":null,"html_url":"https://github.com/biggyspender/ts-functional-pipe","commit_stats":{"total_commits":57,"total_committers":3,"mean_commits":19.0,"dds":0.4385964912280702,"last_synced_commit":"aa6a593a0db2c97c0e6354920270bf1d39b7c003"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biggyspender%2Fts-functional-pipe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biggyspender%2Fts-functional-pipe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biggyspender%2Fts-functional-pipe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biggyspender%2Fts-functional-pipe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/biggyspender","download_url":"https://codeload.github.com/biggyspender/ts-functional-pipe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241019182,"owners_count":19895210,"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":["compose","composed-functions","function-composition","javascript","pipe","point-free","type-safe","typescript"],"created_at":"2025-02-27T13:38:57.823Z","updated_at":"2025-02-27T13:38:58.363Z","avatar_url":"https://github.com/biggyspender.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Type-safe function composition for Typescript\n\n## A micro-library for functional composition\n\nIn the absence of `|\u003e` (the pipe operator) it's useful to have a type-safe pipe function that can compose an a large (up to 64) number of unary functions. This minimal library contains a few different helper functions for this purpose.\n\n[![npm version](http://img.shields.io/npm/v/ts-functional-pipe.svg?style=flat)](https://npmjs.org/package/ts-functional-pipe \"View this project on npm\")\n[![Build Status](https://travis-ci.org/biggyspender/ts-functional-pipe.svg?branch=master)](https://travis-ci.org/biggyspender/ts-functional-pipe)\n\n\u003e NOTE\n\u003e ---\n\u003e \n\u003e Versions \u003c=2.x erroneously used the term `compose` for left-to-right function composition. v3 is a major overhaul of this library and contains several breaking changes, both in the code, and in the meaning of `compose`. \n\u003e\n\u003e These are version \u003e=3 documents. Please find v2.x documentation [here](https://github.com/biggyspender/ts-functional-pipe/tree/v2.1.1)\n\n## Requirements\n\nThis library makes use of [leading/middle rest elements](https://devblogs.microsoft.com/typescript/announcing-typescript-4-2/#non-trailing-rests), introduced in **Typescript version 4.2**\n\n## Usage\n\nSuppose we have the following unary functions:\n\n```typescript\nconst dinosaurify = (name:string) =\u003e `${name}-o-saurus`\nconst sayHello = (name:string) =\u003e `Hello, ${name}!`\n```\n\nWe can compose these functions into a single function using the compose function:\n\n```typescript\nconst sayHelloToDinosaur = compose(sayHello, dinosaurify)\n```\n\nand call it\n\n```typescript\nsayHelloToDinosaur(\"mike\") // \"Hello, mike-o-saurus!\"\n```\n\nNote that with `compose`, function composition occurs from *right-to-left*. \n\nThe `pipe` function composes its parameters from *left-to-right*, so the equivalent `pipe` version of the code above would be:\n\n```typescript\nconst sayHelloToDinosaur_withPipe = pipe(dinosaurify, sayHello)\n```\n\n### The `applyArgs` helper\n\nAlternatively, we could have called the `applyArgs` helper, which is useful for ensuring that type inference flows inutitively through the composed functions. This makes more sense later when we start using it with (apparently) untyped arrow functions.\n\n```typescript\napplyArgs(\"mike\").to(pipe(dinosaurify, sayHello)) // \"Hello, mike-o-saurus!\"\n```\n\nor, less verbosely:\n\n```typescript\napplyArgs(\"mike\")(pipe(dinosaurify, sayHello)) // \"Hello, mike-o-saurus!\"\n```\n\n### `pipeInto` function\n\nThis is shorthand to combine the `applyArgs` helper with `pipe`, reducing the amount of boilerplate. Using `pipeInto` we can rewrite the above as:\n\n```typescript\npipeInto(\"mike\", dinosaurify, sayHello)\n```\n\n\n## In depth\n\nPipes work with **unary**-functions, using the return value of one function as the only parameter to the next function.\n\n### Defining higher-order unary map and filter functions\n\nSay we create our own versions the Array map and filter functions to work over `Iterable\u003cT\u003e`\n\n```typescript\n// helper function for making iterables from generator functions\nconst toIterable = \u003cT, TF extends () =\u003e IterableIterator\u003cT\u003e\u003e(f: TF) =\u003e ({\n  [Symbol.iterator]: f\n})\n\nconst _map = \u003cT, TOut\u003e(src: Iterable\u003cT\u003e, selector: (v: T, i: number) =\u003e TOut): Iterable\u003cTOut\u003e =\u003e\n  toIterable(function*() {\n    let c = 0\n    for (const v of src) {\n      yield selector(v, c++)\n    }\n  })\n\nconst _filter = \u003cT\u003e(src: Iterable\u003cT\u003e, pred: (v: T, i: number) =\u003e boolean): Iterable\u003cT\u003e =\u003e\n  toIterable(function*() {\n    let i = 0\n    for (const x of src) {\n      if (pred(x, i++)) {\n        yield x\n      }\n    }\n  })\n```\n\nHere, the `_map` and `_filter` are not unary functions so cannot be used in a pipe/compose.\n\n### Convert functions to unary with `deferP0`\n\nWe can use the provided `deferP0` method to transform these functions into functions that return a unary function (that takes a single parameter that was the first parameter of the original source function)\n\nSo it turns functions of the form\n\n    (src: TSrc, b: B, c: C, d: D) =\u003e R \n    \ninto functions of the form\n\n    (b: B, c: C, d: D) =\u003e (src: TSrc) =\u003e R\n\n### Functions that return unary functions\n\nSo, to make a composable `map` function:\n\n```typescript\nconst map = deferP0(_map)\n```\n\nHere, we transform the `_map` function with type \n\n\n    \u003cT, TOut\u003e(src: Iterable\u003cT\u003e, selector: (v: T, i: number) =\u003e TOut): Iterable\u003cTOut\u003e \n    \ninto the generated `map` function which has the type \n\n    \u003cT, TOut\u003e(selector: (v: T, i: number) =\u003e TOut) =\u003e (src: Iterable\u003cT\u003e): Iterable\u003cTOut\u003e\n\nAs can be seen, we end up with a function that generates a **unary** function.\n\nWe can do the same with `_filter`\n\n```typescript\nconst filter = deferP0(_filter)\n```\n\nNow the `map` and `filter` functions that we generated above **return** unary functions and can be used in a pipe/compose with type inference \"flowing\" through the composed functions.\n\n### Composing `map` and `filter` with `pipe`\n\nLet's use them with the `pipe` and the `applyArgs` helper (so that type information propagates through all the function parameters)\n\n```typescript\nconst transformed = \n  applyArgs([1, 2, 3]).to(\n    pipe(\n      filter(x =\u003e x % 2 === 1),  // x is inferred as number\n      map(x =\u003e x * 2)            // x is inferred as number\n    )\n  ) // iterable with values [2, 6]\n```\n\nWhen using \"untyped\" arrow functions, as above, by using the `applyArgs` helper, we can see how types are propagated through the functions without needing to provide types for any function parameters. However, we might just want a re-useable function composed of multiple functions, so we can use `compose(...unaryFuncs)` or `pipe(...unaryFuncs)` on their own... but we'll need to supply type-information, usually in just one place, so that typescript can infer other types successfully:\n\n```typescript\nconst oddNumbersMultipliedByTwo =\n    // pipe is inferred as (src:Iterable\u003cnumber\u003e)=\u003eIterable\u003cstring\u003e\n    pipe(\n      // typescript can infer all other types when \n      // we provide this input type annotation (number)\n      filter(x:number =\u003e x % 2 === 1), \n      map(x =\u003e x.toString()),   // x is inferred as number\n      map(x =\u003e x + \" \" + x)     // x is inferred as string\n    )\n```\n\nSo `oddNumbersMultipliedByTwoPipe` has the inferred type\n\n    (src: Iterable\u003cnumber\u003e) =\u003e Iterable\u003cstring\u003e\n\nand when we use it...\n\n```typescript\nconst r = oddMultipliedByTwo([1, 2, 3]) \n// arr has type string[]\nconst arr = [...r] // [\"1 1\", \"2 2\"]\n```\n\n`arr` has type string[]\n\n### acknowledgements\n\nCreated using the wonderful [https://github.com/alexjoverm/typescript-library-starter](https://github.com/alexjoverm/typescript-library-starter).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbiggyspender%2Fts-functional-pipe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbiggyspender%2Fts-functional-pipe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbiggyspender%2Fts-functional-pipe/lists"}