Title: Various issues when trying to use `pipe` function · Issue #29904 · microsoft/TypeScript · GitHub
Open Graph Title: Various issues when trying to use `pipe` function · Issue #29904 · microsoft/TypeScript
X Title: Various issues when trying to use `pipe` function · Issue #29904 · microsoft/TypeScript
Description: TypeScript Version: 3.3.1 Search Terms: pipe compose functional programming composition generics overloads Code It goes without saying that pipe is a very common utility function for composing functions, used often in functional programm...
Open Graph Description: TypeScript Version: 3.3.1 Search Terms: pipe compose functional programming composition generics overloads Code It goes without saying that pipe is a very common utility function for composing func...
X Description: TypeScript Version: 3.3.1 Search Terms: pipe compose functional programming composition generics overloads Code It goes without saying that pipe is a very common utility function for composing func...
Opengraph URL: https://github.com/microsoft/TypeScript/issues/29904
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Various issues when trying to use `pipe` function","articleBody":"\u003c!-- 🚨 STOP 🚨 𝗦𝗧𝗢𝗣 🚨 𝑺𝑻𝑶𝑷 🚨\r\n\r\nHalf of all issues filed here are duplicates, answered in the FAQ, or not appropriate for the bug tracker. Even if you think you've found a *bug*, please read the FAQ first, especially the Common \"Bugs\" That Aren't Bugs section!\r\n\r\nPlease help us by doing the following steps before logging an issue:\r\n * Search: https://github.com/Microsoft/TypeScript/search?type=Issues\r\n * Read the FAQ: https://github.com/Microsoft/TypeScript/wiki/FAQ\r\n\r\nPlease fill in the *entire* template below.\r\n--\u003e\r\n\r\n\u003c!-- Please try to reproduce the issue with `typescript@next`. It may have already been fixed. --\u003e\r\n**TypeScript Version:** 3.3.1\r\n\r\n\u003c!-- Search terms you tried before logging this (so others can find this issue more easily) --\u003e\r\n**Search Terms:** pipe compose functional programming composition generics overloads\r\n\r\n**Code**\r\n\r\nIt goes without saying that `pipe` is a very common utility function for composing functions, used often in functional programming. I've been using a `pipe` function in TypeScript for about 2 years, and over time I've collected numerous issues.\r\n\r\nI wanted to create an issue to collect all of this information, to help others who want to use `pipe` so they are aware of the various footguns—and also for the TypeScript team to help their visibility of these issues.\r\n\r\nTo the best of my ability, I have narrowed these bugs down to the simplest examples, and provided any interesting (and sometimes useful) workarounds. I would appreciate any help in narrowing these examples further—perhaps even combining them where perceived issues are artefacts of the same underlying problems.\r\n\r\nIn my experience, 80% of the time `pipe` just works. These issues cover the remaining 20% of the time. Issue 1 is by far the most significant. The rest are unordered.\r\n\r\nI have linked to sub issues where I'm aware they exist. For those without linked issues, we may want to create new issues to trick them independently.\r\n\r\n## 1. Generics are lost when first composed function is generic\r\n\r\nRelated issues:\r\n- https://github.com/Microsoft/TypeScript/issues/12838\r\n- https://github.com/Microsoft/TypeScript/issues/10247\r\n\r\n```ts\r\ndeclare const pipe: {\r\n \u003cA, B, C\u003e(ab: (a: A) =\u003e B, bc: (b: B) =\u003e C): (a: A) =\u003e C;\r\n};\r\n\r\ntype Component\u003cP\u003e = (props: P) =\u003e {};\r\n\r\ndeclare const myHoc1: \u003cP\u003e(C: Component\u003cP\u003e) =\u003e Component\u003cP\u003e;\r\ndeclare const myHoc2: \u003cP\u003e(C: Component\u003cP\u003e) =\u003e Component\u003cP\u003e;\r\n\r\ndeclare const MyComponent1: Component\u003c{ foo: 1 }\u003e;\r\n\r\n// When `strictFunctionTypes` disabled:\r\n// Expected type: `(a: Component\u003c{ foo: 1 }\u003e) =\u003e Component\u003c{ foo: 1 }\u003e`\r\n// Actual type: `(a: {}) =\u003e Component\u003c{}\u003e`\r\nconst enhance = pipe(\r\n /*\r\n When `strictFunctionTypes` enabled, unexpected type error::\r\n Argument of type '\u003cP\u003e(C: Component\u003cP\u003e) =\u003e Component\u003cP\u003e' is not assignable to parameter of type '(a: {}) =\u003e Component\u003c{}\u003e'.\r\n Types of parameters 'C' and 'a' are incompatible.\r\n Type '{}' is not assignable to type 'Component\u003c{}\u003e'.\r\n Type '{}' provides no match for the signature '(props: {}): {}'.\r\n */\r\n myHoc1,\r\n myHoc2,\r\n);\r\n// Expected type: `Component\u003c{ foo: 1 }\u003e`\r\n// Actual type: `Component\u003c{}\u003e`\r\nconst MyComponent2 = enhance(MyComponent1);\r\n\r\n// Workaround:\r\nconst enhance2 = pipe(\r\n () =\u003e myHoc1(MyComponent1),\r\n myHoc2,\r\n);\r\nconst MyComponent3 = enhance2({});\r\n```\r\n\r\nWith the option suggested in https://github.com/Microsoft/TypeScript/issues/27288, TypeScript would at least alert the developer to change the code to workaround this problem.\r\n\r\n## 2. Incorrect overload is used for `pipe`\r\n\r\n- when `strictFunctionTypes` is disabled\r\n- when first composed function parameter is optional\r\n- when first `pipe` overload is zero parameters for first function\r\n\r\nRelated issues: https://github.com/Microsoft/TypeScript/issues/29913\r\n\r\n```ts\r\ndeclare const pipe: {\r\n // 0-argument first function\r\n // Workaround: disable this overload\r\n \u003cA\u003e(a: () =\u003e A): () =\u003e A;\r\n\r\n // 1-argument first function\r\n \u003cA, B\u003e(ab: (a: A) =\u003e B): (a: A) =\u003e B;\r\n};\r\n\r\n// Expected type: `(a: {} | undefined) =\u003e number`\r\n// Actual type: `() =\u003e number`\r\nconst fn = pipe((_a?: {}) =\u003e 1);\r\n```\r\n\r\n## 3. Inference does not work when first `pipe` overload is zero parameters for first function\r\n\r\nRelated issues:\r\n- https://github.com/Microsoft/TypeScript/issues/25293\r\n- Problem explanation: https://github.com/Microsoft/TypeScript/issues/25293#issuecomment-401229257\r\n\r\n```ts\r\ndeclare const pipe: {\r\n // 0-argument first function\r\n // Workaround: disable this overload\r\n \u003cA, B\u003e(a: () =\u003e A, ab: (a: A) =\u003e B): () =\u003e B;\r\n\r\n // 1-argument first function\r\n \u003cA, B, C\u003e(ab: (a: A) =\u003e B, bc: (b: B) =\u003e C): (a: A) =\u003e C;\r\n};\r\n\r\n// Example 1\r\n\r\ntype Fn = (n: number) =\u003e number;\r\nconst fn: Fn = pipe(\r\n // Expected param `x` type to be inferred as `number`\r\n // Actual type: any\r\n x =\u003e x + 1,\r\n x =\u003e x * 2,\r\n);\r\n\r\n// Example 2\r\n\r\nconst promise = Promise.resolve(1);\r\npromise.then(\r\n pipe(\r\n // Expected param `x` type to be inferred as `number`\r\n // Actual type: any\r\n x =\u003e x + 1,\r\n x =\u003e x * 2,\r\n ),\r\n);\r\n```\r\n\r\n## 4. Untitled\r\n\r\nRelated issues:\r\n- https://github.com/Microsoft/TypeScript/issues/25826\r\n- Problem explanation: https://github.com/Microsoft/TypeScript/issues/25826#issuecomment-411777646\r\n\r\n```ts\r\ndeclare const pipe: {\r\n // Workaround 1: enable this overload\r\n // \u003cA, B, C\u003e(ab: (a: A) =\u003e B, bc: (b: B) =\u003e C): (a: A) =\u003e C;\r\n\r\n \u003cA, B, C, D\u003e(ab: (a: A) =\u003e B, bc: (b: B) =\u003e C, cd: (c: C) =\u003e D): (a: A) =\u003e D;\r\n};\r\n\r\ndeclare const getString: () =\u003e string;\r\ndeclare const orUndefined: (name: string) =\u003e string | undefined;\r\ndeclare const identity: \u003cT\u003e(value: T) =\u003e T;\r\n\r\nconst fn = pipe(\r\n getString,\r\n\r\n /*\r\n Unexpected type error:\r\n Type 'string | undefined' is not assignable to type '{}'.\r\n Type 'undefined' is not assignable to type '{}'.\r\n */\r\n string =\u003e orUndefined(string),\r\n\r\n // Workaround 2: pass the function directly, instead of wrapping:\r\n // get,\r\n\r\n identity,\r\n);\r\n```\r\n\r\n## 5. Incorrect overload is used for composed function\r\n\r\nRelated issues:\r\n- https://github.com/Microsoft/TypeScript/issues/25637\r\n- https://github.com/Microsoft/TypeScript/issues/25637#issuecomment-463375426\r\n\r\n``` ts\r\ndeclare const pipe: {\r\n \u003cA, B, C\u003e(ab: (a: A) =\u003e B, bc: (b: B) =\u003e C): (a: A) =\u003e C;\r\n};\r\n\r\ndeclare const myFn: {\r\n (p: string): string;\r\n (p: any): number;\r\n};\r\n\r\ndeclare const getString: () =\u003e string;\r\n\r\n// Expected type: `(a: {}) =\u003e string`\r\n// Actual type: `(a: {}) =\u003e number`\r\n// Note: if we comment out the last overload for `myFn`, we get the expected\r\n// type.\r\nconst fn = pipe(\r\n getString,\r\n myFn,\r\n);\r\n```\r\n\r\n## 6. Untitled\r\n\r\nRelated issues:\r\n- https://github.com/Microsoft/TypeScript/issues/25791\r\n- https://github.com/Microsoft/TypeScript/issues/25791#issuecomment-463381280\r\n\r\n```ts\r\ndeclare const pipe: {\r\n \u003cA, B, C, D\u003e(ab: (a: A) =\u003e B, bc: (b: B) =\u003e C, cd: (c: C) =\u003e D): (a: A) =\u003e D;\r\n};\r\n\r\ndeclare const getArray: () =\u003e string[];\r\ndeclare const first: \u003cT\u003e(ts: T[]) =\u003e T;\r\n\r\n// When `strictFunctionTypes` disabled:\r\n// Expected type: `(a: {}) =\u003e string`\r\n// Actual type: `(a: {}) =\u003e {}`\r\nconst fn = pipe(\r\n getArray,\r\n x =\u003e x,\r\n /*\r\n When `strictFunctionTypes` enabled, unexpected type error:\r\n Argument of type '\u003cT\u003e(ts: T[]) =\u003e T' is not assignable to parameter of type '(c: {}) =\u003e {}'.\r\n Types of parameters 'ts' and 'c' are incompatible.\r\n Type '{}' is missing the following properties from type '{}[]': length, pop, push, concat, and 25 more.\r\n */\r\n first,\r\n);\r\n\r\n// Workaround 1: use `identity` function\r\ndeclare const identity: \u003cT\u003e(value: T) =\u003e T;\r\nconst fn2 = pipe(\r\n getArray,\r\n identity,\r\n first,\r\n);\r\n\r\n// Workaround 2: wrap last function\r\nconst fn3 = pipe(\r\n getArray,\r\n x =\u003e x,\r\n x =\u003e first(x),\r\n);\r\n```\r\n","author":{"url":"https://github.com/OliverJAsh","@type":"Person","name":"OliverJAsh"},"datePublished":"2019-02-13T21:51:44.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":15},"url":"https://github.com/29904/TypeScript/issues/29904"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:c8d3fade-06f3-f292-b91d-36ca1249aebe |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | B294:13E1D4:457F5A:60047E:6A61AA06 |
| html-safe-nonce | b0f9f37f0660b8fc03fe0f271e1045078fc44a36807f9829a6358d75f7789843 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCMjk0OjEzRTFENDo0NTdGNUE6NjAwNDdFOjZBNjFBQTA2IiwidmlzaXRvcl9pZCI6IjIyNDQwMDIwNDg5NDEwNzQ5NTAiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | dbfd58dba0c46bb522d1d893df7be3251dbd025c7d1bc359864ced8ce339a52d |
| hovercard-subject-tag | issue:410020157 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/microsoft/TypeScript/29904/issue_layout |
| twitter:image | https://opengraph.githubassets.com/26213d0a83ad8a576551c28e83b4dbb4dc8e79bedef6b29b4c85c97b73c71ef4/microsoft/TypeScript/issues/29904 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/26213d0a83ad8a576551c28e83b4dbb4dc8e79bedef6b29b4c85c97b73c71ef4/microsoft/TypeScript/issues/29904 |
| og:image:alt | TypeScript Version: 3.3.1 Search Terms: pipe compose functional programming composition generics overloads Code It goes without saying that pipe is a very common utility function for composing func... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | OliverJAsh |
| hostname | github.com |
| expected-hostname | github.com |
| None | 6f4633bcf01c1ad14b73fd07dd39ac31d61f3d3c2578ee08ec1792b7b351eeb9 |
| turbo-cache-control | no-preview |
| go-import | github.com/microsoft/TypeScript git https://github.com/microsoft/TypeScript.git |
| octolytics-dimension-user_id | 6154722 |
| octolytics-dimension-user_login | microsoft |
| octolytics-dimension-repository_id | 20929025 |
| octolytics-dimension-repository_nwo | microsoft/TypeScript |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 20929025 |
| octolytics-dimension-repository_network_root_nwo | microsoft/TypeScript |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | ac296ae7f21856f1f92adbad22f870b6fbb4b907 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width