{"id":19763955,"url":"https://github.com/scalarhq/nominal","last_synced_at":"2025-04-13T10:57:44.404Z","repository":{"id":48524517,"uuid":"516921153","full_name":"scalarhq/nominal","owner":"scalarhq","description":"🔒 The new way to do types in typescript.","archived":false,"fork":false,"pushed_at":"2023-03-22T22:28:47.000Z","size":17,"stargazers_count":262,"open_issues_count":4,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-27T02:11:37.291Z","etag":null,"topics":["type-safety","types","typescript"],"latest_commit_sha":null,"homepage":"https://zackoverflow.dev/writing/nominal-and-refinement-types-typescript","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scalarhq.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2022-07-23T00:36:19.000Z","updated_at":"2025-03-06T13:51:12.000Z","dependencies_parsed_at":"2024-11-12T04:11:51.259Z","dependency_job_id":"96c1f6df-59aa-45a7-8c8a-75b4322e2b58","html_url":"https://github.com/scalarhq/nominal","commit_stats":null,"previous_names":["scalarhq/nominal"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalarhq%2Fnominal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalarhq%2Fnominal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalarhq%2Fnominal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalarhq%2Fnominal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scalarhq","download_url":"https://codeload.github.com/scalarhq/nominal/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248703194,"owners_count":21148117,"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":["type-safety","types","typescript"],"created_at":"2024-11-12T04:11:48.461Z","updated_at":"2025-04-13T10:57:44.384Z","avatar_url":"https://github.com/scalarhq.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nominal\nThe right way to do types in typescript.\n\n## Installation\n\n```bash\nnpm install nominal-types\n\nyarn install nominal-types\n\npnpm install nominal-types\n```\n\n# Usage\n\n## Basic\nThe most immediate benefit of nominal types is preventing confusion between two types. In regular Typescript \nyou run into this problem:\n```ts\ntype Minutes = number\ntype Seconds = number\nconst minutesToSeconds = (minutes: Minutes) =\u003e minutes * 60\n\nconst seconds: Seconds = 420\n// uh-oh, we can use Minutes and Seconds interchangeably\nminutesToSeconds(seconds)\n```\n\nNominal types solve this problem\n```ts\nimport { Nominal, nominal } from 'nominal-types';\n\ntype Minutes = Nominal\u003c'Minutes', number\u003e;\ntype Seconds = Nominal\u003c'Seconds', number\u003e;\n\nconst minutesToSeconds = (minutes: Minutes) =\u003e minutes * 60\n\n// You can directly type cast or use nominal.make\nconst seconds = nominal.make\u003cSeconds\u003e(420)\nconst minutes = 1337 as Minutes\n\n// doesn't work, yay type safety\nminutesToSeconds(seconds)\n// does work!\nminutesToSeconds(minutes)\n```\n\n## Another example\nYou can use nominal types to give your code even better type-safety guarantees. \n\nThis goes **beyond just type-safety**, it's a performance optimization: once you know the array is sorted, you never have to sort it again. This is enforcing that at a type level.\n\n\n```typescript\ntype SortedArray\u003cT\u003e = Nominal\u003c'sortedArray', Array\u003cT\u003e\u003e\n\nconst sort = \u003cT\u003e(arr: Array\u003cT\u003e): SortedArray\u003cT\u003e =\u003e arr.sort()\n\nconst binarySearch = \u003cT\u003e(\n  sorted: SortedArray\u003cT\u003e,\n  search: T\n): number | undefined  =\u003e {\n    /* ... */\n}\n\nconst regularArray = [1, 7, 2, 3, 6, 9, 10, 4, 5]\n// won't work\nbinarySearch(regularArray, 2)\n// will work\nbinarySearch(sort(regularArray), 3)\n```\n\nThis is also known as [Refinement types](https://en.wikipedia.org/wiki/Refinement_type)\n\n\n\n## Composing types\n\nWe can actually make this a bit crazier, we can compose nominal types\n\n```ts\n\ntype SortedArray\u003cT\u003e = Nominal\u003c'sortedArray', Array\u003cT\u003e\u003e\n\nconst sort = \u003cT\u003e(arr: Array\u003cT\u003e): SortedArray\u003cT\u003e =\u003e arr.sort() as SortedArray\u003cT\u003e\n\nconst nonEmpty = \u003cT\u003e(arr:Array\u003cT\u003e):NonEmptyArray\u003cT\u003e =\u003e arr.filter(Boolean) as NonEmptyArray\u003cT\u003e\n\ntype NonEmptyArray\u003cK, T extends Array\u003cK\u003e\u003e = Nominal\u003c'nonEmptyArray', T\u003e;\ntype NonEmptySorted\u003cT\u003e = NonEmptyArray\u003cT, SortedArray\u003cT\u003e\u003e;\n\nconst binarySearch = \u003cT\u003e(sorted: NonEmptySorted\u003cT\u003e): T =\u003e {\n  let foo = sorted[0]\n  return foo\n}\n\n// won't work\nbinarySearch(regularArray)\n// still won't work\nbinarySearch(sort(regularArray))\n\nbinarySearch(nonEmpty(sort(regularArray)))\n\n```\n\n## Examples\n\nMore examples in [examples folder](./examples), you can also see them typed on replit.\n\n| Example     | Link                                                      |\n|-------------|-----------------------------------------------------------|\n| basic       |    https://replit.com/@CryogenicPlanet/Nominal#basic.ts   |\n| sorting     |    https://replit.com/@CryogenicPlanet/Nominal#sort.ts    |\n| composing   | https://replit.com/@CryogenicPlanet/Nominal#composing.ts  |\n| safeRecords | https://replit.com/@CryogenicPlanet/Nominal#safeRecord.ts |\n\n## Credits\n\nYou can read more about this https://zackoverflow.dev/writing/nominal-and-refinement-types-typescript\n\nInspiration from [Ghosts of Departed Proofs (Functional Pearl)](https://kataskeue.com/gdp.pdf)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscalarhq%2Fnominal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscalarhq%2Fnominal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscalarhq%2Fnominal/lists"}