{"id":18840019,"url":"https://github.com/alexsasharegan/safe-types","last_synced_at":"2025-09-08T16:47:13.817Z","repository":{"id":29297396,"uuid":"121165796","full_name":"alexsasharegan/safe-types","owner":"alexsasharegan","description":" Type safe utils inspired from the Rust language for writing better TypeScript/Flow.","archived":false,"fork":false,"pushed_at":"2023-01-06T01:33:10.000Z","size":4063,"stargazers_count":50,"open_issues_count":19,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-26T01:56:31.967Z","etag":null,"topics":["algebraic-data-types","combinatorics","flow","javascript","monad","rust","safety","typescript"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/safe-types","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/alexsasharegan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-02-11T20:49:39.000Z","updated_at":"2024-03-11T06:31:03.000Z","dependencies_parsed_at":"2023-01-14T14:37:30.724Z","dependency_job_id":null,"html_url":"https://github.com/alexsasharegan/safe-types","commit_stats":null,"previous_names":[],"tags_count":80,"template":false,"template_full_name":null,"purl":"pkg:github/alexsasharegan/safe-types","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexsasharegan%2Fsafe-types","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexsasharegan%2Fsafe-types/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexsasharegan%2Fsafe-types/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexsasharegan%2Fsafe-types/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexsasharegan","download_url":"https://codeload.github.com/alexsasharegan/safe-types/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexsasharegan%2Fsafe-types/sbom","scorecard":{"id":182488,"data":{"date":"2025-08-11","repo":{"name":"github.com/alexsasharegan/safe-types","commit":"51739475961a53730e817e4ed041d405a2a35e37"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"50 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-4w2v-q235-vp99","Warn: Project is vulnerable to: GHSA-cph5-m8f7-6c5x","Warn: Project is vulnerable to: GHSA-wf5p-g6vw-rhxx","Warn: Project is vulnerable to: GHSA-jr5f-v2jv-69x6","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-ff7x-qrg7-qggm","Warn: Project is vulnerable to: GHSA-74fj-2j2h-c42q","Warn: Project is vulnerable to: GHSA-pw2r-vq6v-hr8c","Warn: Project is vulnerable to: GHSA-jchw-25xp-jwwc","Warn: Project is vulnerable to: GHSA-cxjh-pqwp-8mfp","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-vfrc-7r7c-w9mx","Warn: Project is vulnerable to: GHSA-7wwv-vh3v-89cq","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-4rq4-32rv-6wp6","Warn: Project is vulnerable to: GHSA-64g7-mvw6-v9qj","Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-38fc-wpqx-33j7","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T19:07:16.998Z","repository_id":29297396,"created_at":"2025-08-16T19:07:16.998Z","updated_at":"2025-08-16T19:07:16.998Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274217974,"owners_count":25243354,"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","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["algebraic-data-types","combinatorics","flow","javascript","monad","rust","safety","typescript"],"created_at":"2024-11-08T02:44:46.146Z","updated_at":"2025-09-08T16:47:13.798Z","avatar_url":"https://github.com/alexsasharegan.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Safe Types\n\n[![npm](https://img.shields.io/npm/v/safe-types.svg?style=for-the-badge)](https://img.shields.io/npm/v/safe-types)\n[![npm downloads](https://img.shields.io/npm/dt/safe-types.svg?style=for-the-badge)](https://www.npmjs.com/package/safe-types)\n[![GitHub issues](https://img.shields.io/github/issues/alexsasharegan/safe-types.svg?style=for-the-badge)](https://github.com/alexsasharegan/safe-types/issues)\n[![Travis](https://img.shields.io/travis/alexsasharegan/safe-types.svg?style=for-the-badge)](https://github.com/alexsasharegan/safe-types)\n[![Coverage Status](https://img.shields.io/coveralls/github/alexsasharegan/safe-types.svg?style=for-the-badge)](https://coveralls.io/github/alexsasharegan/safe-types)\n[![GitHub license](https://img.shields.io/github/license/alexsasharegan/safe-types.svg?style=for-the-badge)](https://github.com/alexsasharegan/safe-types/blob/master/LICENSE.md)\n\nType safe utils inspired from the Rust language for writing better JavaScript.\nWritten in typescript with support for flow definitions.\n\n## API Documentation\n\n[Safe Types API documentation](https://sad-saha-4a5616.netlify.com/)\n\nFollow the link for method references. Below is an explanation of _why_ and\n_how_.\n\n## Purpose\n\nThis library started out as an experiment both to learn Rust concepts as well as\nto determine whether some of Rust's types can be mapped to TypeScript and\nimprove the safety of TypeScript/JavaScript.\n\n**Side Note:** It's my opinion that a library like this requires a 100%\nTypeScript environment to provide security around JS types. Without the\nTypeScript compiler and tooling, these primitives may make your data more opaque\nrather than provide insight and clarity into the many states application data\ncan be in. Using an editor like vscode can provide some built in intellisense\nwhen running in JavaScript without TypeScript, but inference is limited.\n\n## Concepts\n\nThe two main exports of this library are implementations of the Maybe Monad and\nthe Either Monad. I wrote this library without any formal learning in\n[category theory](https://en.wikipedia.org/wiki/Category_theory), so the Rust\nstandard library was essential to guiding my implementation of these Monad\npatterns. While these functional programming concepts are notoriously arcane,\nthey are much easier to learn in practice than in theory.\n\n### Option Type _(Maybe Monad)_\n\nImagine if you could rewrite JavaScript and remove `null` and `undefined` types.\nThe dreaded error `undefined is not a function` could be gone forever!. Sounds\ngreat, right? But there are still many times when you need a way to represent\nnothing. What happens if your function doesn't return anything? It turns out\nthat there is a pattern for doing with type safety.\n\nThe option type is a simple type that can exist in one of two states--having\n_**some**_ value, or having _**none**_ (nothing). In a way, this is very similar\nto having a value or having `null`. However, an option carries the context of\nthe type it represent regardless of it being present. Instead of calling a\nfunction expecting a `string` or `null`, you receive an option of a string\n`Option\u003cstring\u003e`. That option _**may be**_ `Some\u003cstring\u003e`, or it _**may be**_\n`None`. This is why it's known as the Maybe Monad.\n\nSo how does this work? We wrap the state of having a value in a box. In\nJavaScript, this box is implemented with a plain old JavaScript object (POJO).\nLet's imagine what a `Some\u003cstring\u003e` would look like for the string\n`'typescript'`:\n\n```ts\ntype Some\u003cT\u003e = { value: T };\n\nlet someString: Some\u003cstring\u003e = { value: \"typescript\" };\n```\n\nThis is fine for a simple box to hold our value, but how would we represent\n`None`? What about an empty object?\n\n```ts\ntype None = {};\n\nlet noneString: None = {};\n```\n\nNow we have a consistent object shape, so we need a way to distinguish the state\nof the option type. We want to keep our option generic, so it can't be\nimplemented with any kind of value checking on the `value` property of the\n`Some` object. Instead, we can use another property that will be common to both\nstates that we call a type discriminant.\n\n```ts\ntype Some\u003cT\u003e = {\n  value: T;\n  state: \"Some\";\n};\ntype None = {\n  state: \"None\";\n};\n```\n\nThis is enough for us to implement the option type! But keeping track of the\noption state is tedious, and the abstraction shouldn't allow us to know the\ninternal state. That's where the library comes in.\n\n#### Usage\n\nGenerally, usage of the option type starts by wrapping a value of which we know\nthe type, but we also know that it may return `null` or `undefined`. To do that,\nwe can use the `Option.of` or `Option.from` methods (one is just an alias).\n\n```ts\nfunction getAtIndex(index, list) {\n  return Option.of(list[index]);\n}\n\nlet list = [1, 2, 3];\nlet firstElement = getAtIndex(0, list);\n// Some\u003c1\u003e\nlet probablyNot = getAtIndex(10, list);\n// None\n```\n\nBoth `Option.of` and its alias only create a `Some` type if the value is not\n`null` or `undefined`. From then on, you can make use of the methods available\non the `Option` type to perform operations safely. Let's try another example\nlike getting the input value from an HTMLInputElement.\n\n```ts\n// document.querySelector returns an element or null\nlet maybeInput = Option.of(document.querySelector(\"input[name=email]\"));\n\n// If we are certain this exists, we can unwrap the value.\n// It will throw an error if the option is a `none`\nlet input = maybeInput.unwrap();\n// We can also have it throw a custom error message\ninput = maybeInput.expect(\"expected an input element with name=email\");\n\n// We can safely read a value if we provide a default\nlet value = maybeInput.map_or(\"\", element =\u003e element.value);\n\n// We can even do a chain of dependent actions without dealing with null!\nlet maybeSubmitBtn = maybeInput.and_then(element =\u003e\n  Option.of(element.parent.querySelector(\"button[type=submit]\"))\n);\n```\n\nYou can browse the set of methods known as _combinators_, but the most important\nmethod is `match`. The `match` method accepts an object with methods for each\npossible state of the option (`Some` and `None`) and must return the same type\nfor each possibility.\n\n```ts\nlet luckyNumber = Option.of(list.find(num =\u003e num \u003e 100)).match({\n  // Called with the value.\n  Some(num) {\n    return num;\n  },\n  None() {\n    return 0;\n  },\n});\n```\n\n## Convert Tasks to Observables\n\nThe Task object is similar to RxJS observables, and can be converted in just a\nfew lines of code.\n\n```ts\nconst fetchTask = new Task(async ({ Ok, Err }) =\u003e {\n  try {\n    const response = await fetch(\"https://example.test/some/url\");\n    const data = await response.json();\n    return Ok(data);\n  } catch (error) {\n    return Err(error);\n  }\n});\n\nconst fetchStream = new Observable(async subscriber =\u003e {\n  await fetchTask.fork({\n    Ok: data =\u003e subscriber.next(data),\n    Err: error =\u003e subscriber.error(error),\n  });\n\n  subscriber.complete();\n});\n```\n\nIt is important to note that while the Task model maps nicely to Observables, it\nis missing the mechanisms to cancel its work. For this reason, there is no\nteardown function returned from the subscribe function passed to the\nObservable's constructor.\n\nThe lack of cancellation gets at the key differences between Tasks and\nObservables.\n\n- Tasks model a single chain of asynchronous work that may produce a value;\n  Observables model a stream of asynchronous values/signals.\n- Tasks do not cancel since they are more like lazy promises; Observables manage\n  a resource lifecycle including their construction, completion, \u0026 destruction.\n- Tasks model type-safe errors; Observables treat errors opaquely since they are\n  caught and therefore cannot provide type guarantees.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexsasharegan%2Fsafe-types","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexsasharegan%2Fsafe-types","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexsasharegan%2Fsafe-types/lists"}