{"id":28212465,"url":"https://github.com/snowflyt/kind-adt","last_synced_at":"2026-02-21T18:15:05.137Z","repository":{"id":288722399,"uuid":"969019648","full_name":"Snowflyt/kind-adt","owner":"Snowflyt","description":"🪴 The kind of ADTs you can count on in TypeScript","archived":false,"fork":false,"pushed_at":"2025-10-03T04:04:20.000Z","size":694,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-03T04:59:47.802Z","etag":null,"topics":["adt","algebraic-data-types","higher-kinded-types","hkt","pattern-matching","type-safe","type-safety","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/kind-adt","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Snowflyt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"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,"zenodo":null}},"created_at":"2025-04-19T07:42:44.000Z","updated_at":"2025-10-03T04:03:01.000Z","dependencies_parsed_at":"2025-04-19T13:54:03.249Z","dependency_job_id":"329dced8-bcd5-44fe-9a64-9f1a655db1ac","html_url":"https://github.com/Snowflyt/kind-adt","commit_stats":null,"previous_names":["snowflyt/kind-adt"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/Snowflyt/kind-adt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Snowflyt%2Fkind-adt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Snowflyt%2Fkind-adt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Snowflyt%2Fkind-adt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Snowflyt%2Fkind-adt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Snowflyt","download_url":"https://codeload.github.com/Snowflyt/kind-adt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Snowflyt%2Fkind-adt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29689653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T15:51:39.154Z","status":"ssl_error","status_checked_at":"2026-02-21T15:49:03.425Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["adt","algebraic-data-types","higher-kinded-types","hkt","pattern-matching","type-safe","type-safety","typescript"],"created_at":"2025-05-17T19:08:52.336Z","updated_at":"2026-02-21T18:15:05.125Z","avatar_url":"https://github.com/Snowflyt.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003ekind-adt\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  🪴 The \u003cstrong\u003ekind\u003c/strong\u003e of \u003cstrong\u003eADTs\u003c/strong\u003e you can count on in \u003cstrong\u003eTypeScript\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/kind-adt\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/kind-adt.svg\" alt=\"downloads\" height=\"18\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/kind-adt\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/kind-adt.svg\" alt=\"npm version\" height=\"18\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://bundlephobia.com/package/kind-adt\"\u003e\n    \u003cimg src=\"https://img.shields.io/bundlephobia/minzip/kind-adt.svg\" alt=\"minzipped size\" height=\"18\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://coveralls.io/github/Snowflyt/kind-adt?branch=main\"\u003e\n    \u003cimg src=\"https://img.shields.io/coverallsCoverage/github/Snowflyt/kind-adt?branch=main\" alt=\"coverage status\" height=\"18\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/Snowflyt/kind-adt\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/l/kind-adt.svg\" alt=\"MPL-2.0 license\" height=\"18\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n```typescript\nimport { type Data, make } from \"kind-adt\";\nimport type { Arg0, HKT } from \"hkt-core\";\n\n// Define an ADT\nexport type Option\u003cT\u003e = Data\u003c{\n  Some: [value: T];\n  None: [];\n}\u003e;\n\n// Generate constructors and match functions for the ADT\nexport const { Some, None, match } = make\u003cOptionHKT\u003e();\ninterface OptionHKT extends HKT { // \u003c- Lift it to HKT\n  return: Option\u003cArg0\u003cthis\u003e\u003e;\n}\n\nfunction safeDivide(n: number, d: number) {\n  //     ^?: (n: number, d: number) =\u003e Option\u003cnumber\u003e\n  if (d === 0) return None;\n  return Some(n / d);\n}\n\n// Pattern matching for ADTs\nmatch(safeDivide(42, 2), {\n  Some: (n) =\u003e console.log(\"Result:\", n),\n  None: () =\u003e console.log(\"Division by zero!\"),\n});\n```\n\n## Features\n\n- _One_ type to define your **A**lgebraic **D**ata **T**ype (`Data`).\n- _One_ function to create **constructors**, **deconstructors**, **type guards** and **pattern matching function** for your ADT with **type safety** (`make`).\n- Support for **functional pipelines** with a [`.pipe()` method](#functional-pipelines-with-pipe) on all ADTs.\n- [**Readable type signatures**](#provide-more-readable-type-signatures) for your ADT with _labeled tuples_.\n- [**Recursive ADTs**](#recursive-adts) with ease.\n- Tiny footprint (~2kB minzipped).\n- Convert your ADTs to human-readable strings with [showify](https://github.com/Snowflyt/showify) integration.\n\n## Installation\n\nTo install **kind-adt** via npm (or any other package manager you prefer):\n\n```shell\nnpm install kind-adt\n```\n\n## Quickstart\n\n**ADTs** ([**A**lgebraic **D**ata **T**ypes](https://en.wikipedia.org/wiki/Algebraic_data_type)) are just **_discriminated unions_** in TypeScript.\n\n```typescript\nimport type { Data } from \"kind-adt\";\n\nexport type Option\u003cT\u003e = Data\u003c{\n  Some: [value: T];\n  None: [];\n}\u003e;\n// Expands to:\n// export type Option\u003cT\u003e =\n//   | { readonly _tag: \"Some\"; readonly _0: T }\n//   | { readonly _tag: \"None\" }\n```\n\n\u003cdiv align=\"right\"\u003e\n  \u003cp\u003e\u003cstrong\u003eWhat is a \u003ci\u003ediscriminated union\u003c/i\u003e?\u003c/strong\u003e\u003c/p\u003e\n  \u003cp\u003eThese are types that can represent one of several variants, and you can determine which variant it is by looking at a special property called a \u003cstrong\u003ediscriminant\u003c/strong\u003e (in this case, \u003ccode\u003e_tag\u003c/code\u003e).\u003c/p\u003e\n\u003c/div\u003e\n\nYou can create **constructors**, **deconstructors** and **type guards** using the **HKT** (**H**igher-**K**inded **T**ype) of your ADT.\n\n```typescript\nimport { make } from \"kind-adt\";\nimport { show } from \"showify\"; // Optional dependency\nimport type { Arg0, HKT } from \"hkt-core\";\n\nexport const { Some, None, match: matchOption, isSome, ifSome /* ... */ } = make\u003cOptionHKT\u003e();\ninterface OptionHKT extends HKT {\n  return: Option\u003cArg0\u003cthis\u003e\u003e;\n}\n\nSome(42); // =\u003e { _tag: \"Some\", _0: 42 }\n// ^?: \u003cnumber\u003e(args_0: number) =\u003e Option\u003cnumber\u003e\n\n// Use `show` from showify to print the ADT in a readable format\nconsole.log(show(Some(42), { indent: 2, trailingComma: \"auto\", colors: true }));\n// Some(42)\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e➡️ You can use \u003ccode\u003emake\u003c/code\u003e with \u003cstrong\u003enon-generic ADTs\u003c/strong\u003e, which doesn’t require an HKT.\u003c/summary\u003e\n\n```typescript\nexport type IpAddr = Data\u003c{\n  V4: [number, number, number, number];\n  V6: [string];\n}\u003e;\n\nexport const IpAddr = make\u003cIpAddr\u003e();\n\nIpAddr.V4(127, 0, 0, 1); // =\u003e { _tag: \"V4\", _0: 127, _1: 0, _2: 0, _3: 1 }\nIpAddr.V6(\"::1\"); // =\u003e { _tag: \"V6\", _0: \"::1\" }\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e➡️ Performance note on \u003ccode\u003emake\u003c/code\u003e\u003c/summary\u003e\n\n`make` uses [proxies](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to “generate” related functions at runtime, which may introduce some performance overhead. If you are concerned about performance, or is developing a library that needs to be as fast as possible, you should provide the names of each _variant_ manually, which eliminates the need for proxies.\n\n```typescript\nexport const Option = make\u003cOptionHKT\u003e([\"Some\", \"None\"]);\n\n// Use it the same way as before\n```\n\n\u003c/details\u003e\n\n\u003cdiv align=\"right\"\u003e\n  \u003cp\u003e\u003cstrong\u003eWhat is a \u003ci\u003eHigher-Kinded Type\u003c/i\u003e?\u003c/strong\u003e\u003c/p\u003e\n  \u003cp\u003eYou can think of it as a \u003cstrong\u003e\u003ci\u003etype-level function\u003c/i\u003e\u003c/strong\u003e that takes one or more types and returns a type. In this case, \u003ccode\u003eOptionHKT\u003c/code\u003e is a type-level function that takes one type (\u003ccode\u003eArg0\u0026lt;this\u0026gt;\u003c/code\u003e) and returns a type (\u003ccode\u003eOption\u0026lt;Arg0\u0026lt;this\u0026gt;\u0026gt;\u003c/code\u003e), which can be represented as \u003ccode\u003eType -\u003e Type\u003c/code\u003e.\u003c/p\u003e\n  \u003cp\u003ekind-adt itself does not export an HKT implementation, but it accepts any HKT implementation that satisfies the \u003cstrong\u003e\u003ca href=\"https://github.com/Snowflyt/hkt-core\"\u003ehkt-core\u003c/a\u003e\u003c/strong\u003e V1 standard. You can directly use the one exported from \u003ca href=\"https://github.com/Snowflyt/hkt-core\"\u003ehkt-core\u003c/a\u003e, or \u003ca href=\"#i-dont-want-to-install-another-package-for-hkt-can-i-implement-my-own\"\u003eimplement your own\u003c/a\u003e — if neither suits your needs, just creating constructors manually is also an option.\u003c/p\u003e\n\u003c/div\u003e\n\nWith the magic of HKT, these generated constructors are already **_generic_** and **_type-safe_**.\n\n```typescript\n// The return type is inferred as `Option\u003cnumber\u003e`\nfunction safeDivide(n: number, d: number) {\n  //     ^?: (n: number, d: number) =\u003e Option\u003cnumber\u003e\n  if (d === 0) return None;\n  return Some(n / d);\n}\n```\n\n\u003cdiv align=\"right\"\u003e\n  \u003cp\u003e\u003cstrong\u003eI can directly use \u003ccode\u003eNone\u003c/code\u003e as an \u003ccode\u003eOption\u003c/code\u003e \u003ci\u003ewithout calling it with any arguments\u003c/i\u003e?\u003c/strong\u003e\u003c/p\u003e\n  \u003cp\u003eRight! Constructors themselves have a \u003ccode\u003e_tag\u003c/code\u003e property that is the discriminant of the ADT, so if a constructor has no arguments, it is \u003cstrong\u003ealready a valid \u003ci\u003evariant\u003c/i\u003e\u003c/strong\u003e of the ADT.\u003c/p\u003e\n  \u003cp\u003eWhile \u003ccode\u003eNone\u003c/code\u003e is a valid \u003ccode\u003eOption\u003c/code\u003e, you can still call it with a generic argument to change the return type in TypeScript, e.g., \u003ccode\u003eNone\u0026lt;string\u0026gt;()\u003c/code\u003e will return an \u003ccode\u003eOption\u0026lt;string\u0026gt;\u003c/code\u003e.\u003c/p\u003e\n\u003c/div\u003e\n\nOnce you have your ADT, you can **pattern match** on it with the generated `match` function!\n\n```typescript\nfunction getOrElse\u003cT\u003e(opt: Option\u003cT\u003e, defaultValue: T): T {\n  return matchOption(opt, {\n    Some: (value) =\u003e value,\n    None: () =\u003e defaultValue,\n  });\n}\n```\n\n\u003cdiv align=\"right\"\u003e\n  \u003cp\u003e\u003cstrong\u003eIt’s \u003ci\u003enot\u003c/i\u003e real pattern matching, actually.\u003c/strong\u003e\u003c/p\u003e\n  \u003cp\u003eThat’s right. Pattern matching normally includes support for nested patterns, guards, and more, but kind-adt only provides a simple pattern matching function that is more like a \u003cstrong\u003eswitch\u003c/strong\u003e statement in TypeScript. Also check out kind-adt’s sister project \u003ca href=\"https://github.com/Snowflyt/megamatch\"\u003emegamatch\u003c/a\u003e for a more powerful pattern matching library that has built-in support for kind-adt style ADTs.\u003c/p\u003e\n  \u003cp\u003e\u003cstrong\u003eSometimes I only want to match \u003ci\u003ea single variant\u003c/i\u003e of an ADT, any syntax like \u003ccode\u003eif let\u003c/code\u003e in Rust?\u003c/strong\u003e\u003c/p\u003e\n  \u003cp\u003eAs shown in the example, \u003ccode\u003emake\u0026lt;OptionHKT\u0026gt;()\u003c/code\u003e also generates \u003ccode\u003eifSome\u003c/code\u003e and \u003ccode\u003eifNone\u003c/code\u003e. While not mentioned in this quickstart guide, \u003ccode\u003emake\u003c/code\u003e generates many more helper functions than just constructors and matchers, including \u003ccode\u003eif*\u003c/code\u003e, \u003ccode\u003eis*\u003c/code\u003e, \u003ccode\u003eunwrap*\u003c/code\u003e, and more. Check the \u003ca href=\"#type-guards-and-unwrap\"\u003etype guards section\u003c/a\u003e and the \u003ca href=\"#conditional-deconstructors-if\"\u003econditional deconstructors section\u003c/a\u003e for more details.\u003c/p\u003e\n\u003c/div\u003e\n\n`match` also provides a curried overload that can be useful when combined with the `pipe` utility from libraries like [Effect](https://github.com/Effect-TS/effect) or [fp-ts](https://github.com/gcanti/fp-ts).\n\n```typescript\nimport { pipe } from \"effect\";\n\npipe(\n  Some(42),\n  matchOption({\n    Some: (n) =\u003e n * 2,\n    None: () =\u003e 0,\n  }),\n); // =\u003e 84\n```\n\nSee also the [functional pipelines with `.pipe()`](#functional-pipelines-with-pipe) section for details on kind-adt’s built-in alternative to external pipe utilities.\n\nNote that `ADT.match` requires the return type of each case to be the same. To allow different return types, you can use the `ADT.matchW` function, where the `W` suffix stands for _wider_. `ADT.matchW` can be used the same way as `ADT.match`, supporting both curried and non-curried overloads.\n\n\u003cdiv align=\"right\"\u003e\n  \u003cp\u003e\u003cstrong\u003eWhat if I want to handle multiple variants of an ADT in a single case?\u003c/strong\u003e\u003c/p\u003e\n  \u003cp\u003ekind-adt doesn’t support this feature directly, but allows you to use a “catch-all” case to handle the remaining variants of an ADT. (while our sister project \u003ca href=\"https://github.com/Snowflyt/megamatch\"\u003emegamatch\u003c/a\u003e does support this feature, with built-in support for kind-adt style ADTs)\u003c/p\u003e\n\u003c/div\u003e\n\n`ADT.match(W)` performs exhaustiveness checking that requires you to handle all variants of an ADT. If you want to handle only some variants of an ADT and leave the rest to the default case, you can use a “catch-all” case with a `_` wildcard:\n\n```typescript\nmatchOption(Some(42), {\n  Some: (n) =\u003e n * 2,\n  _: () =\u003e 0, // Catch-all case\n});\n```\n\n\u003cdiv align=\"right\"\u003e\n  \u003cp\u003e\u003cstrong\u003eWhat’s next?\u003c/strong\u003e\u003c/p\u003e\n\u003c/div\u003e\n\n- The type signatures of generated functions are very readable, but you can [improve the readability of the type signatures with labeled tuples](#provide-more-readable-type-signatures).\n- Check out the [syntax sugar for ADTs with only one object field](#syntax-sugar-for-adts-with-only-one-object-field) and [recursive ADTs](#recursive-adts).\n- See how to [check the type of an ADT with **type guards** and extract the fields with **`unwrap`**](#type-guards-and-unwrap).\n- Check out the [conditional deconstructors](#conditional-deconstructors-if) if you are tired with using `match` on a single variant with a verbose catch-all case.\n- See how to use [showify](#convert-adts-to-human-readable-strings) to convert your ADTs to human-readable strings.\n\n## Recipes\n\n### Extract variant types\n\nYou can extract the type of each variant of an ADT using the `Tagged` utility type.\n\n```typescript\nimport type { Data, Tagged } from \"kind-adt\";\n\ntype Option\u003cT\u003e = Data\u003c{\n  Some: [value: T];\n  None: [];\n}\u003e;\n\ntype Some\u003cT\u003e = Extract\u003cOption\u003cT\u003e, Tagged\u003c\"Some\"\u003e\u003e;\n// Expands to:\n// type Some\u003cT\u003e = {\n//   readonly _tag: \"Some\";\n//   readonly _0: T;\n// }\ntype None = Extract\u003cOption\u003cunknown\u003e, Tagged\u003c\"None\"\u003e\u003e;\n// Expands to:\n// type None = {\n//   readonly _tag: \"None\"\n// }\n```\n\n### Functional pipelines with `.pipe()`\n\nEvery ADT in kind-adt supports a `.pipe()` method, allowing for a more functional and fluent style of programming similar to libraries like [Effect](https://github.com/Effect-TS/effect) or [RxJS](https://rxjs.dev/api/index/function/pipe).\n\n```typescript\nimport { type Data, make } from \"kind-adt\";\nimport type { Arg0, HKT } from \"hkt-core\";\n\ntype Option\u003cT\u003e = Data\u003c{\n  Some: [value: T];\n  None: [];\n}\u003e;\n\nconst { Some, None, match } = make\u003cOptionHKT\u003e();\ninterface OptionHKT extends HKT {\n  return: Option\u003cArg0\u003cthis\u003e\u003e;\n}\n\nconst map: \u003cT, R\u003e(fn: (value: T) =\u003e R) =\u003e (opt: Option\u003cT\u003e) =\u003e Option\u003cR\u003e = (fn) =\u003e\n  match({\n    Some: (value) =\u003e Some(fn(value)),\n    None: () =\u003e None,\n  });\n\n// Using pipe with an ADT\nSome(42).pipe(\n  map((n) =\u003e n + 1),\n  map((n) =\u003e n * 2),\n  match({\n    Some: (n) =\u003e console.log(\"Result:\", n),\n    None: () =\u003e console.log(\"No value\"),\n  }),\n);\n//\u003e Result: 86\n```\n\nUnder the hood, any value returned by a constructor already has a `.pipe()` method, which takes a sequence of functions and applies them one after another, with each function receiving the result of the previous function.\n\nYou can also create your own objects with the same `.pipe()` functionality using the exported `Pipeable` interface and `PipeableProto`:\n\n```typescript\nimport { type Pipeable, PipeableProto } from \"kind-adt\";\n\n// Create a pipeable object\nconst myData = Object.create(PipeableProto);\nmyData.value = 42;\n\n// Use the pipe method\nconst result = myData.pipe(\n  (obj) =\u003e obj.value * 2,\n  (n) =\u003e n.toString(),\n); // =\u003e \"84\"\n```\n\nAlternatively, you can import the `Pipeable` constructor, which has its prototype set to `PipeableProto`:\n\n```typescript\nimport { Pipeable } from \"kind-adt\";\n\nclass MyData extends Pipeable {\n  constructor(public value: number) {\n    super();\n  }\n}\n\nconst data = new MyData(42);\n\ndata instanceof Pipeable; // =\u003e true\n\ndata.pipe(\n  (data) =\u003e data.value * 2,\n  (n) =\u003e n.toString(),\n); // =\u003e \"84\"\n```\n\n`ADT` is also exported as a constructor with its prototype set to `ADTProto`, which extends `PipeableProto`. See the [Add your own methods to ADTs](#add-your-own-methods-to-adts) section for details.\n\n### Provide more readable type signatures\n\nLet’s revisit the `Option\u003cT\u003e` example in the quickstart section.\n\n```typescript\ntype Option\u003cT\u003e = Data\u003c{\n  Some: [value: T];\n  None: [];\n}\u003e;\n\nconst Option = make\u003cOptionHKT\u003e();\ninterface OptionHKT extends HKT {\n  return: Option\u003cArg0\u003cthis\u003e\u003e;\n}\n\nOption.Some(42);\n//     ^?: \u003cnumber\u003e(args_0: number) =\u003e Option\u003cnumber\u003e\n```\n\nIn this case, the generated constructor `Some` has a type signature of `\u003cT\u003e(args_0: T) =\u003e Option\u003cT\u003e` instead of the more readable `\u003cT\u003e(value: T) =\u003e Option\u003cT\u003e`. This naming issue also affects other functions like `Option.match`, `Option.unwrap`, etc. We can improve the readability of these type signatures by using TypeScript [_labeled tuples_](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#labeled-tuple-elements).\n\n```typescript\ntype Option\u003cT\u003e = Data\u003c\n  Some: [value: T],\n  None: [],\n\u003e;\n```\n\nAnd that’s it! Now all generated functions will have a more readable type signature.\n\n```typescript\nfunction Option.Some\u003cT\u003e(value: T): Option\u003cT\u003e;\n\nfunction Option.match\u003cT, R\u003e(adt: Option\u003cT\u003e, cases: {\n  readonly Some: (value: T) =\u003e R;\n  readonly None: () =\u003e R;\n}): R;\n\n// ...\n```\n\nThis also applies to other ADTs, such as `Result`, `Either`, etc.\n\n```typescript\ntype Result\u003cT, E\u003e = Data\u003c{\n  Ok: [value: T];\n  Err: [error: E];\n}\u003e;\n\ntype Either\u003cA, B\u003e = Data\u003c{\n  Left: [value: A];\n  Right: [value: B];\n}\u003e;\n```\n\n### Syntax sugar for ADTs with only one object field\n\nIf your ADT has only one object field, declaring it like this would be a little tedious:\n\n```typescript\ntype Tree\u003cT\u003e = Data\u003c{\n  Empty: [];\n  Node: [{ value: T; left: Tree\u003cT\u003e; right: Tree\u003cT\u003e }];\n}\u003e;\n// Expands to:\n// type Tree\u003cT\u003e =\n//   | { readonly _tag: \"Empty\" }\n//   | { readonly _tag: \"Node\"; readonly _0: { value: T; left: Tree\u003cT\u003e; right: Tree\u003cT\u003e } }\n```\n\nTo make it easier to declare, kind-adt provides syntax sugar for this case. You can declare an ADT with a bare object literal type instead of a tuple literal type. This serves as a shorthand for a tuple type with a single object field.\n\n```typescript\ntype Tree\u003cT\u003e = Data\u003c{\n  Empty: {}; // In this case, `{}` is equivalent to `[]`\n  Node: { value: T; left: Tree\u003cT\u003e; right: Tree\u003cT\u003e };\n}\u003e;\n// Expands to:\n// type Tree\u003cT\u003e =\n//   | { readonly _tag: \"Empty\" }\n//   | { readonly _tag: \"Node\"; readonly _0: { value: T; left: Tree\u003cT\u003e; right: Tree\u003cT\u003e } }\n```\n\nThen you can use it like this:\n\n```typescript\ninterface TreeHKT extends HKT {\n  return: Tree\u003cArg0\u003cthis\u003e\u003e;\n}\n\nconst Tree = make\u003cTreeHKT\u003e();\n\nconst depth: \u003cT\u003e(tree: Tree\u003cT\u003e) =\u003e number = Tree.match({\n  Empty: () =\u003e 0,\n  Node: ({ left, right }) =\u003e 1 + Math.max(depth(left), depth(right)),\n});\n```\n\n### Recursive ADTs\n\nThe `Tree\u003cT\u003e` example above is already a recursive ADT — TypeScript naturally supports recursive types when they’re defined using object literal types.\n\nHowever, things get a little tricky when you want to declare the `Node` variant with 3 fields (`value`, `left`, and `right`) directly instead of using an object as the only field:\n\n```typescript\ntype Tree\u003cT\u003e = Data\u003c{\n  // ~~~~\n  // Type alias 'Tree' circularly references itself. ts(2456)\n  Empty: [];\n  Node: [value: T, left: Tree\u003cT\u003e, right: Tree\u003cT\u003e];\n}\u003e;\n```\n\nThis type error originates from the internal evaluation mechanism of TypeScript: types like interfaces, object literal types and function return types are “lazily” evaluated (evaluated only when necessary), while type aliases are “eagerly” evaluated (evaluated immediately). See [this Stack Overflow answer](https://stackoverflow.com/questions/37233735/interfaces-vs-types-in-typescript/77669722#77669722) for more details.\n\nIn our scenario, this means that when TypeScript tries to evaluate the `Tree\u003cT\u003e` type alias, it will eagerly evaluate the `Node` variant, which references `Tree\u003cT\u003e` itself, causing a circular reference error.\n\nTo avoid this, kind-adt provides an alternative syntax to declare recursive ADTs like this, where an object literal type with numeric keys is used as an alternative to a tuple type:\n\n```typescript\ntype Tree\u003cT\u003e = Data\u003c{\n  Empty: [];\n  Node: { 0: T; 1: Tree\u003cT\u003e; 2: Tree\u003cT\u003e };\n}\u003e;\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e➡️ Click to see how to add labels to the fields of a recursive ADT\u003c/summary\u003e\n\nTo add labels to the fields (see [Provide more readable type signatures](#provide-more-readable-type-signatures)), you can use a magical field `__labels` that is a labeled tuple exists only in the type system:\n\n```typescript\ntype Tree\u003cT\u003e = Data\u003c{\n  Empty: [];\n  Node: {\n    __labels: [value: void, left: void, right: void];\n    0: T;\n    1: Tree\u003cT\u003e;\n    2: Tree\u003cT\u003e;\n  };\n}\u003e;\n```\n\nThe type of each element in `__labels` does not matter, only the labels are used to provide better type signatures for the generated functions.\n\n\u003c/details\u003e\n\nThen you can use it like this:\n\n```typescript\ninterface TreeHKT extends HKT {\n  return: Tree\u003cArg0\u003cthis\u003e\u003e;\n}\n\nconst Tree = make\u003cTreeHKT\u003e();\n\nconst depth: \u003cT\u003e(tree: Tree\u003cT\u003e) =\u003e number = Tree.match({\n  Empty: () =\u003e 0,\n  Node: (_, left, right) =\u003e 1 + Math.max(depth(left), depth(right)),\n});\n```\n\n### Type guards and `unwrap`\n\nWhile `ADT.match(W)` is a powerful tool for handling ADTs, sometimes you simply need to check if an ADT is a specific variant and extract its value. For these cases, kind-adt provides `ADT.is*` and `ADT.unwrap*` functions.\n\n```typescript\nconst Option = make\u003cOptionHKT\u003e();\n\nfunction getOrElse\u003cT\u003e(opt: Option\u003cT\u003e, defaultValue: T): T {\n  if (Option.isSome(opt)) return opt._0;\n  return defaultValue;\n}\n```\n\nHowever, accessing the fields of an ADT with `._${number}` like this is not very readable, so kind-adt also provides `ADT.unwrap*` functions to extract the fields of an ADT into a tuple:\n\n```typescript\nfunction getOrElse\u003cT\u003e(opt: Option\u003cT\u003e, defaultValue: T): T {\n  if (Option.isSome(opt)) {\n    const [value] = Option.unwrap(opt);\n    return value;\n  }\n  return defaultValue;\n}\n\nfunction depth\u003cT\u003e(tree: Tree\u003cT\u003e): number {\n  if (Tree.isEmpty(tree)) return 0;\n  const [_, left, right] = Tree.unwrap(tree);\n  return 1 + Math.max(depth(left), depth(right));\n}\n```\n\nYou can also use `ADT.unwrap*` like `Option.unwrapSome` to extract the value of a specific variant of an ADT, which will throw a runtime error if the ADT is not of that variant.\n\nA standalone `unwrap` function is exported directly from kind-adt, which can be useful if you want to handle any ADT without knowing its type at compile time.\n\n```typescript\nimport { unwrap } from \"kind-adt\";\n\nconst [value] = unwrap(Some(42));\n```\n\n### Conditional deconstructors (`if*`)\n\nYou might often need to write code like this:\n\n```typescript\nOption.match(safeDivide(42, 2), {\n  Some: (n) =\u003e console.log(\"This is a very long message that I want to log\", n),\n  _ =\u003e {},\n});\n```\n\nSince the `match` function performs exhaustiveness checking, you have to provide a catch-all case `_` to handle the remaining variants of the ADT. This can be awkward and a waste of space when your codebase is full of such cases.\n\nkind-adt provides `ADT.if*` functions to handle this case more elegantly, similar to [the `if let` syntax in Rust](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html):\n\n```typescript\nOption.ifSome(safeDivide(42, 2), (n) =\u003e {\n  console.log(\"This is a very long message that I want to log\", n);\n});\n```\n\nIf the ADT matches the specified variant, the callback function will be called with the value of that variant, otherwise nothing will happen.\n\nThe `if*` function also has a return type: if the matching succeeds, the return type will be the return type of the callback function, otherwise it will be `void` (`undefined` in JavaScript).\n\n```typescript\nconst result = Option.ifSome(safeDivide(42, 2), (n) =\u003e {\n  //  ^?: number | void\n  console.log(\"This is a very long message that I want to log\", n);\n  return n;\n});\n```\n\nYou can also provide an optional second argument to the `if*` function, which is a callback function that will be called if the matching fails. If it is provided, the return type of the `if*` function will be the return type of either the first or the second callback function, depending on whether the matching succeeds or fails.\n\n```typescript\nconst result = Option.ifSome(\n  //  ^?: number | string\n  safeDivide(42, 2),\n  (n) =\u003e {\n    console.log(\"This is a very long message that I want to log\", n);\n    return n;\n  },\n  () =\u003e {\n    console.log(\"This is a very long message that I want to log\");\n    return \"default value\";\n  },\n);\n```\n\n### Convert ADTs to human-readable strings\n\nkind-adt provides integration with the [showify](https://github.com/Snowflyt/showify) package, which allows you to convert objects to human-readable strings. This is especially useful for debugging and logging purposes.\n\nTo use this feature, you need to install the [showify](https://github.com/Snowflyt/showify) package:\n\n```shell\nnpm install showify\n```\n\nThen you can use the `show` function to convert an ADT to a human-readable string.\n\n```javascript\nimport { show } from \"showify\";\n\n// Suppose we have an ADT `data Tree\u003cT\u003e = Empty | Node(T, Tree\u003cT\u003e, Tree\u003cT\u003e)`\nconst tree = Tree.Node(\n  1,\n  Tree.Node(2, Tree.Node(3, Tree.Empty, Tree.Empty), Tree.Empty),\n  Tree.Node(4, Tree.Empty, Tree.Node(3, Tree.Empty, Tree.Empty)),\n);\n\n// Print the ADT with ANSI colors and indented format\nconsole.log(show(tree, { indent: 2, trailingComma: \"auto\", colors: true }));\n// Node(\n//   1,\n//   Node(2, Node(3, Empty, Empty), Empty),\n//   Node(4, Empty, Node(3, Empty, Empty))\n// )\n```\n\nIf you find it tedious to write `console.log(show(...))` every time, you can create a utility function to print the ADT directly:\n\n```javascript\nimport { show } from \"showify\";\n\nexport function println(...args: unknown[]) {\n  console.log(\n    ...args.map((arg) =\u003e\n      typeof arg === \"string\" ? arg : show(arg, { colors: true, trailingComma: \"auto\", indent: 2 }),\n    ),\n  );\n}\n```\n\n### Add your own methods to ADTs\n\n\u003e [!WARNING]\n\u003e\n\u003e This feature is not recommended for most users, as it may lead to unexpected behavior while module resolution. Use it at your own risk.\n\nADTs and ADT constructors in kind-adt use a prototype chain to provide “methods” like `.pipe()`. Due to the nature of how prototypes work in JavaScript, you can add your own methods (or “[monkey patch](https://en.wikipedia.org/wiki/Monkey_patch)”) to ADTs or ADT constructors by modifying the prototype of the ADT or ADT constructor.\n\nkind-adt exports four prototypes for you to use, including the `PipeableProto` mentioned in the [Functional pipelines with `.pipe()`](#functional-pipelines-with-pipe) section:\n\n```typescript\nimport {\n  type Pipeable,\n  PipeableProto,\n  type PipeableFunction,\n  PipeableFunctionProto,\n  type ADT,\n  ADTProto,\n  type ADTConstructor,\n  ADTConstructorProto,\n} from \"kind-adt\";\n```\n\nEach `XxxProto` has a related `Xxx` type, which is the type of the object that can be created from the prototype. ADTs and ADT constructors in kind-adt follow the following prototype chain:\n\n```typescript\n╔══════════════════════════════════╗   ╔════════════╗   ╔═════════════════╗\n║ Concrete ADT (e.g., `Some(...)`) ║ ← ║ `ADTProto` ║ ← ║ `PipeableProto` ║\n╚══════════════════════════════════╝   ╚════════════╝   ╚═════════════════╝\n\n╔═════════════════════════════════════════╗   ╔═══════════════════════╗   ╔═════════════════════════╗\n║ Concrete ADT constructor (e.g., `None`) ║ ← ║ `ADTConstructorProto` ║ ← ║ `PipeableFunctionProto` ║\n╚═════════════════════════════════════════╝   ╚═══════════════════════╝   ╚═════════════════════════╝\n```\n\nNote that though ADTs and ADT constructors share the same `.pipe()` method, they actually have independent prototype chains. This means that you have to patch both prototypes if you want to add your own methods to both ADTs and ADT constructors.\n\nThe following example shows how to add a `.println()` method to add ADTs and ADT constructors with type safety. We’ll create a `patches.ts` file to add the method to the prototypes of ADTs and ADT constructors.\n\n```typescript\n// patches.ts\nimport { type ADT, ADTProto, type ADTConstructor, ADTConstructorProto } from \"kind-adt\";\nimport { show } from \"showify\";\n\n/* Make TypeScript aware of the new method with a module augmentation\n   https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation */\ndeclare module \"kind-adt\" {\n  interface ADT {\n    println(): void;\n  }\n\n  interface ADTConstructor {\n    println(): void;\n  }\n}\n\n/* Add the method to the ADT prototype */\nADTProto.println = function println(this: ADT) {\n  console.log(show(this, { colors: true, trailingComma: \"auto\", indent: 2 }));\n};\nADTConstructorProto.println = function println(this: ADTConstructor) {\n  console.log(show(this, { colors: true, trailingComma: \"auto\", indent: 2 }));\n};\n\n/* Ensure TypeScript treats this file as a module */\nexport {};\n```\n\nThen you can import the `patches.ts` file in your main file to apply the patches:\n\n```typescript\nimport \"./patches\"; // Import the patches file\n\nimport { type Data, make } from \"kind-adt\";\n\ntype Option\u003cT\u003e = Data\u003c{\n  Some: [value: T];\n  None: [];\n}\u003e;\n\nconst { Some, None } = make\u003cOptionHKT\u003e();\ninterface OptionHKT extends HKT {\n  return: Option\u003cArg0\u003cthis\u003e\u003e;\n}\n\nSome(42).println(); // Prints: Some(42)\nNone.println(); // Prints: None\n```\n\n## FAQ\n\n### I don’t want to install another package for HKT. Can I implement my own?\n\nThe [**hkt-core**](https://github.com/Snowflyt/hkt-core) V1 standard is simple enough to implement by yourself. By extending `HKT` exported from **hkt-core**, you only get two additional properties (`~hkt` and `signature`), which can be easily defined manually.\n\n```typescript\ntype Args\u003cF\u003e = F extends { Args: (_: infer A extends unknown[]) =\u003e void } ? A : never;\n\n// No need to extend `HKT` ↙\ninterface OptionHKT {\n  // ↙ Required by the standard\n  \"~hkt\": { version: 1 };\n  // ↓ This defines the type signature (kind) of the HKT\n  signature: (type: unknown) =\u003e Option\u003cunknown\u003e;\n  // You can use types other than `unknown` if your type parameters are constrained,\n  // to provide better type safety.\n  // signature: (type: string | number) =\u003e Option\u003cstring | number\u003e\n  // ↓ The same as before\n  return: Option\u003cArgs\u003cthis\u003e[0]\u003e;\n}\n\n// No need to extend `HKT2` ↙\ninterface ResultHKT {\n  \"~hkt\": { version: 1 };\n  // ↓ Since this HKT has two type parameters, the signature should accept two arguments\n  signature: (type1: unknown, type2: unknown) =\u003e Result\u003cunknown, unknown\u003e;\n  return: Result\u003cArgs\u003cthis\u003e[0], Args\u003cthis\u003e[1]\u003e;\n}\n```\n\nYou can also define your own `HKT` and `HKT2` types if you don’t want to specify all these properties manually every time.\n\n```typescript\ninterface HKT\u003cType = unknown\u003e {\n  \"~hkt\": { version: 1 };\n  signature: (type: Type) =\u003e unknown;\n}\n\ninterface HKT2\u003cType1 = unknown, Type2 = unknown\u003e {\n  \"~hkt\": { version: 1 };\n  signature: (type1: Type1, type2: Type2) =\u003e unknown;\n}\n```\n\n### Why “Kind”?\n\n\u003cstrong\u003e\u003ci\u003eKind\u003c/i\u003e\u003c/strong\u003e is [a term used in type theory](\u003chttps://en.wikipedia.org/wiki/Kind_(type_theory)\u003e) to describe the “type of a type”, or the “type of a type constructor (i.e. HKT)”. For example, the kind of `number`, `Option\u003cstring\u003e` and `Result\u003cnumber, string\u003e` are all `Type`, while the kind of `OptionHKT` is `Type -\u003e Type`, and the kind of `ResultHKT` is `(Type, Type) -\u003e Type`.\n\nThe name kind-adt is a play on words, combining the term “kind” with “ADT” to emphasize the usage of HKTs in defining ADTs.\n\n### ADTs in kind-adt are _incompatible_ with those in Effect or fp-ts!\n\nThat’s true — instead of using `\"_${number}\"` as field names, these libraries use a more descriptive field name like `\"value\"` or `\"error\"`. The design of not following this convention in kind-adt is intentional to allow a cleaner way to match multiple fields in `match` without the need for object destructuring. The use of unreadable field names also encourage users to use `match` instead of directly accessing fields.\n\nCheck [ts-adt](https://github.com/pfgray/ts-adt) if you want to use a more compatible ADT library with Effect or fp-ts.\n\n## License\n\nThis project is licensed under the Mozilla Public License Version 2.0 (MPL 2.0).\nFor details, please refer to the `LICENSE` file.\n\nIn addition to the open-source license, a commercial license is available for proprietary use.\nIf you modify this library and do not wish to open-source your modifications, or if you wish to use the modified library as part of a closed-source or proprietary project, you must obtain a commercial license.\n\nFor details, see `COMMERCIAL_LICENSE.md`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnowflyt%2Fkind-adt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsnowflyt%2Fkind-adt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnowflyt%2Fkind-adt/lists"}