[
  {
    "path": ".gitignore",
    "content": "node_modules"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"[typescriptreact]\": {\n    \"editor.defaultFormatter\": \"rome.rome\"\n  },\n  \"[typescript]\": {\n    \"editor.defaultFormatter\": \"rome.rome\"\n  },\n  \"[json]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  }\n}\n"
  },
  {
    "path": "Readme.md",
    "content": "# 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) => 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<'Minutes', number>;\ntype Seconds = Nominal<'Seconds', number>;\n\nconst minutesToSeconds = (minutes: Minutes) => minutes * 60\n\n// You can directly type cast or use nominal.make\nconst seconds = nominal.make<Seconds>(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<T> = Nominal<'sortedArray', Array<T>>\n\nconst sort = <T>(arr: Array<T>): SortedArray<T> => arr.sort()\n\nconst binarySearch = <T>(\n  sorted: SortedArray<T>,\n  search: T\n): number | undefined  => {\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<T> = Nominal<'sortedArray', Array<T>>\n\nconst sort = <T>(arr: Array<T>): SortedArray<T> => arr.sort() as SortedArray<T>\n\nconst nonEmpty = <T>(arr:Array<T>):NonEmptyArray<T> => arr.filter(Boolean) as NonEmptyArray<T>\n\ntype NonEmptyArray<K, T extends Array<K>> = Nominal<'nonEmptyArray', T>;\ntype NonEmptySorted<T> = NonEmptyArray<T, SortedArray<T>>;\n\nconst binarySearch = <T>(sorted: NonEmptySorted<T>): T => {\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)"
  },
  {
    "path": "examples/basic.ts",
    "content": "// After Nominal\nimport { Nominal } from '../src';\nimport { Equal, Expect } from '@type-challenges/utils';\n\ntype Minutes = Nominal<'Minutes', number>;\ntype Seconds = Nominal<'Seconds', number>;\n\nconst minutesToSeconds = (minutes: Minutes): Seconds =>\n  (minutes * 60) as Seconds;\n\n// You can directly type cast or use nominal.make\nconst seconds = 420 as Seconds;\nconst minutes = 1337 as Minutes;\n\n// @ts-expect-error - doesn't work, yay type safety\nminutesToSeconds(seconds);\n\n// does work!\nminutesToSeconds(minutes);\n\n// @ts-expect-error -  won't work, yay!\nminutesToSeconds(minutesToSeconds(minutes));\n"
  },
  {
    "path": "examples/composing.ts",
    "content": "import { Nominal } from '../src';\n\ntype SortedArray<T> = Nominal<'sortedArray', T[]>;\n\nconst sort = <T>(arr: T[]): SortedArray<T> => arr.sort() as SortedArray<T>;\n\nconst nonEmpty = <K, T extends K[]>(arr: T): NonEmptyArray<K, T> => {\n  if (arr.length === 0) {\n    throw new Error('Array is empty');\n  }\n  return arr as NonEmptyArray<K, T>;\n};\n\ntype NonEmptyArray<K, T extends K[]> = Nominal<'nonEmptyArray', T>;\ntype NonEmptySorted<T> = NonEmptyArray<T, SortedArray<T>>;\n\n// Not implemented\nconst binarySearch = <T>(sorted: NonEmptySorted<T>): T => {\n  let foo = sorted[0];\n  // @ts-ignore\n  return foo;\n};\n\nconst regularArray = [1, 2, 3];\n\n// @ts-expect-error - won't work\nbinarySearch(regularArray);\n// @ts-expect-error - still won't work\nbinarySearch(sort(regularArray));\n\n// will work\nbinarySearch(nonEmpty(sort(regularArray)));\n"
  },
  {
    "path": "examples/proportionalityConstant.ts",
    "content": "import { Equal, Expect } from '@type-challenges/utils';\n\nimport {\n  divideWithK,\n  multiplyWithK,\n  Nominal,\n  ProportionalityConstant,\n} from '../src';\n\nexport type Pixel = Nominal<'Pixel', number>;\nexport type Seconds = Nominal<'Seconds', number>;\nexport type PixelPerSecond = ProportionalityConstant<Pixel, Seconds>;\n\nconst PIXEL_PER_SECOND: PixelPerSecond = 1 as PixelPerSecond;\n\nconst pixels = 100 as Pixel;\nconst seconds = 1 as Seconds;\n\nconst a = multiplyWithK(PIXEL_PER_SECOND, seconds);\ntype A = typeof a;\n\n// @ts-expect-error - should not be able to divide with seconds\nconst _b = divideWithK(PIXEL_PER_SECOND, seconds);\n\nconst c = divideWithK(PIXEL_PER_SECOND, pixels);\ntype C = typeof c;\n\ntype _cases = [Expect<Equal<A, Pixel>>, Expect<Equal<C, Seconds>>];\n"
  },
  {
    "path": "examples/safeRecord.ts",
    "content": "import { Nominal } from '../src';\n\ntype SafeRecord<V, R extends string = ''> = Nominal<\n  'safeRecordSymbol',\n  Record<R, V>\n>;\n\nconst newRecord = <Value extends any, Record extends string>(\n  record: Record,\n  value: Value,\n): SafeRecord<Value, Record> =>\n  ({ [record]: value }) as SafeRecord<Value, Record>;\n\nconst add = <V, R extends string, R2 extends string>(\n  record: SafeRecord<V, R>,\n  key: R2,\n  value: V,\n): SafeRecord<V, R | R2> => {\n  Object.assign(record, { [key]: value });\n\n  return record as SafeRecord<V, R | R2>;\n};\n\nconst safeRecord = newRecord('a', 'b');\n\n// @ts-expect-error -  Won't work\nconst x = safeRecord['random']; // any\n\nconst lmao = add(safeRecord, 'lmao', 'nice');\n\nlmao.a; // string\nlmao.lmao; // string\n\n// @ts-expect-error -  Won't work\nlmao['random']; // any -  Untyped\n"
  },
  {
    "path": "examples/sort.ts",
    "content": "import { Nominal } from '../src';\n\ntype SortedArray<T> = Nominal<'sortedArray', T[]>;\n\nconst sort = <T>(arr: T[]): SortedArray<T> => arr.sort() as SortedArray<T>;\n\nconst binarySearch = <T>(\n  sorted: SortedArray<T>,\n  search: T,\n): number | undefined => {\n  if (sorted.length !== 0) {\n    const midPoint = sorted.length / 2;\n    if (sorted[midPoint] === search) {\n      return midPoint;\n    }\n\n    // Bang: Midpoint will exist\n    if (search > sorted[midPoint]!) {\n      return binarySearch(sorted.slice(midPoint) as SortedArray<T>, search);\n    } else {\n      return binarySearch(sorted.slice(0, midPoint) as SortedArray<T>, search);\n    }\n  } else {\n    return undefined;\n  }\n};\n\nconst regularArray = [1, 7, 2, 3, 6, 9, 10, 4, 5];\n// @ts-expect-error  won't work\nbinarySearch(regularArray, 2);\n// will work\nbinarySearch(sort(regularArray), 2);\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"nominal-types\",\n  \"version\": \"0.2.0\",\n  \"description\": \"Nominal types for better typesafety\",\n  \"main\": \"src/index.ts\",\n  \"types\": \"src/index.ts\",\n  \"keywords\": [\n    \"nominal\",\n    \"typesafety\",\n    \"custom\",\n    \"ids\"\n  ],\n  \"author\": \"modfy\",\n  \"license\": \"MIT\",\n  \"files\": [\n    \"src/\"\n  ],\n  \"devDependencies\": {\n    \"@type-challenges/utils\": \"^0.1.1\",\n    \"rome\": \"^10.0.1\",\n    \"typescript\": \"^5.0.2\"\n  }\n}\n"
  },
  {
    "path": "rome.json",
    "content": "{\n  \"linter\": {\n    \"enabled\": true,\n    \"rules\": {\n      \"recommended\": true\n    }\n  },\n  \"formatter\": {\n    \"enabled\": true,\n    \"indentStyle\": \"space\"\n  },\n  \"javascript\": {\n    \"formatter\": {\n      \"quoteStyle\": \"single\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "const M = Symbol();\n\nexport type Nominal<Name extends string, Type> = Type & {\n  readonly [M]: [Name];\n};\n\nexport * from './standardLib';\nexport * from './proportionalityConstant';\n"
  },
  {
    "path": "src/proportionalityConstant.ts",
    "content": "// export type xPERy<X, Y> =\n\n/**\n * ProportionalityConstant is a type that represents a constant that is used to convert from one type to another\n *\n * Y ∝ X -> Y = λX >  =kX\n *\n * This type is the type of the constant k in the above equation, it can used with the multiplyWithK and divideWithK functions to convert between the two types\n *\n * @example type PixelPerSecond = ProportionalityConstant<Pixel, Seconds>\n *\n * const pixels: Pixels = multiplyWithK(PIXEL_PER_SECOND, 1 as Seconds)\n *\n * const seconds: Seconds = divideWithK(PIXEL_PER_SECOND, 1 as Pixels)\n *\n */\nexport type ProportionalityConstant<\n  Y extends number,\n  X extends number,\n> = number & {\n  readonly __X: X;\n  readonly __Y: Y;\n};\n\ntype multiplyWithKReturn<K> = K extends ProportionalityConstant<\n  infer Y,\n  infer _X\n>\n  ? Y\n  : never;\n\ntype multiplyWithKInferX<K> = K extends ProportionalityConstant<\n  infer _Y,\n  infer X\n>\n  ? X\n  : never;\n\ntype divideWithKReturn<K> = K extends ProportionalityConstant<infer _Y, infer X>\n  ? X\n  : never;\n\ntype divideWithKInferY<K> = K extends ProportionalityConstant<infer Y, infer _X>\n  ? Y\n  : never;\n\n/**\n * A function to get the converted typed after multiplying with proportionality constant\n *\n * @param k - ProportionalityConstant<Y,X> -> Y ∝ X -> Y = λX -> Y = kX\n * @param x - Value of type X\n * @returns y - Value of type Y\n */\nexport const multiplyWithK = <K extends number>(\n  k: K,\n  x: multiplyWithKInferX<K>,\n): multiplyWithKReturn<K> => (k * x) as multiplyWithKReturn<K>;\n\n/**\n * A function to get the converted typed after dividing with proportionality constant\n *\n * @param k - ProportionalityConstant<Y,X> -> Y ∝ X -> X = Y/k\n * @param y - Value of type Y\n * @returns x - Value of type X\n */\nexport const divideWithK = <K extends ProportionalityConstant<any, any>>(\n  k: K,\n  y: divideWithKInferY<K>,\n): divideWithKReturn<K> => (y / k) as divideWithKReturn<K>;\n"
  },
  {
    "path": "src/standardLib.ts",
    "content": "import { Nominal } from './index';\n\n/***\n * This is a replacement for Object.keys which actually types the keys correctly\n *\n * In javascript Object.keys makes the keys strings, so this can only be used with the record is using string keys\n */\nexport const keys = <T extends string>(obj: Record<T, any>): T[] =>\n  Object.keys(obj) as T[];\n\n/**\n * Add values of the same type, a way to add values of some nominal of number can get the sum of the values typed correctly\n */\nexport const plus = <T extends number>(...arg: T[]): T => {\n  const sum = arg.reduce((acc, cur) => acc + cur, 0);\n  return sum as T;\n};\n\n/**\n * Subtract values of the same type, a way to subtract values of some nominal of number can get the difference of the values typed correctly\n */\nexport const minus = <T extends number>(a: T, b: T): T => {\n  return (a - b) as T;\n};\n\n/**\n * Decorate number as negative value\n */\nexport type Negative<T extends number> = Nominal<'Negative', T>;\n/**\n * Create a number decorated as negative, will throw an error if the number is not negative\n */\nexport const Negative = <T extends number>(value: T): Negative<T> => {\n  if (value > 0) {\n    throw new Error('Value must be negative');\n  }\n  return value as Negative<T>;\n};\n\n/**\n * Decorate number as zero\n */\nexport type Zero<T extends number> = Nominal<'Zero', T>;\n\n/**\n * Decorate number as negative or zero\n */\nexport type NegativeOrZero<T extends number> = Negative<T> | Zero<T>;\n/**\n * Decorate number as negative or zero\n *\n * Will throw an error if the number is positive\n */\nexport const NegativeOrZero = <T extends number>(\n  value: T,\n): NegativeOrZero<T> => {\n  if (value > 0) {\n    throw new Error('Value must be negative or zero');\n  }\n  return value as NegativeOrZero<T>;\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t/* Visit https://aka.ms/tsconfig.json to read more about this file */\n\t\t/* Basic Options */\n\t\t// \"incremental\": true,                         /* Enable incremental compilation */\n\t\t\"target\": \"ESNEXT\", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */\n\t\t\"module\": \"ESNext\", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */\n\t\t// \"lib\": [],                                   /* Specify library files to be included in the compilation. */\n\t\t// \"allowJs\": true,                             /* Allow javascript files to be compiled. */\n\t\t// \"checkJs\": true,                             /* Report errors in .js files. */\n\t\t// \"jsx\": \"preserve\",                           /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */\n\t\t// \"declaration\": true,                         /* Generates corresponding '.d.ts' file. */\n\t\t// \"declarationMap\": true,                      /* Generates a sourcemap for each corresponding '.d.ts' file. */\n\t\t// \"sourceMap\": true,                           /* Generates corresponding '.map' file. */\n\t\t// \"outFile\": \"./\",                             /* Concatenate and emit output to single file. */\n\t\t// \"outDir\": \"./\",                              /* Redirect output structure to the directory. */\n\t\t// \"rootDir\": \"./\",                             /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */\n\t\t// \"composite\": true,                           /* Enable project compilation */\n\t\t// \"tsBuildInfoFile\": \"./\",                     /* Specify file to store incremental compilation information */\n\t\t// \"removeComments\": true,                      /* Do not emit comments to output. */\n\t\t\"noEmit\": true,                              /* Do not emit outputs. */\n\t\t// \"importHelpers\": true,                       /* Import emit helpers from 'tslib'. */\n\t\t// \"downlevelIteration\": true,                  /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */\n\t\t// \"isolatedModules\": true,                     /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */\n\t\t/* Strict Type-Checking Options */\n\t\t\"strict\": true, /* Enable all strict type-checking options. */\n\t\t// \"noImplicitAny\": true,                       /* Raise error on expressions and declarations with an implied 'any' type. */\n\t\t// \"strictNullChecks\": true,                    /* Enable strict null checks. */\n\t\t// \"strictFunctionTypes\": true,                 /* Enable strict checking of function types. */\n\t\t// \"strictBindCallApply\": true,                 /* Enable strict 'bind', 'call', and 'apply' methods on functions. */\n\t\t// \"strictPropertyInitialization\": true,        /* Enable strict checking of property initialization in classes. */\n\t\t// \"noImplicitThis\": true,                      /* Raise error on 'this' expressions with an implied 'any' type. */\n\t\t// \"alwaysStrict\": true,                        /* Parse in strict mode and emit \"use strict\" for each source file. */\n\t\t/* Additional Checks */\n\t\t// \"noUnusedLocals\": true,                      /* Report errors on unused locals. */\n\t\t// \"noUnusedParameters\": true,                  /* Report errors on unused parameters. */\n\t\t// \"noImplicitReturns\": true,                   /* Report error when not all code paths in function return a value. */\n\t\t// \"noFallthroughCasesInSwitch\": true,          /* Report errors for fallthrough cases in switch statement. */\n\t\t// \"noUncheckedIndexedAccess\": true,            /* Include 'undefined' in index signature results */\n\t\t// \"noImplicitOverride\": true,                  /* Ensure overriding members in derived classes are marked with an 'override' modifier. */\n\t\t// \"noPropertyAccessFromIndexSignature\": true,  /* Require undeclared properties from index signatures to use element accesses. */\n\t\t/* Module Resolution Options */\n\t\t\"moduleResolution\": \"node\", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */\n\t\t// \"baseUrl\": \"./\",                             /* Base directory to resolve non-absolute module names. */\n\t\t// \"paths\": {},                                 /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */\n\t\t// \"rootDirs\": [],                              /* List of root folders whose combined content represents the structure of the project at runtime. */\n\t\t\"typeRoots\": [\n\t\t\t\"./node_modules/@types\",\n\t\t], /* List of folders to include type definitions from. */\n\t\t// \"types\": [],                                 /* Type declaration files to be included in compilation. */\n\t\t// \"allowSyntheticDefaultImports\": true,        /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */\n\t\t\"esModuleInterop\": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */\n\t\t// \"preserveSymlinks\": true,                    /* Do not resolve the real path of symlinks. */\n\t\t// \"allowUmdGlobalAccess\": true,                /* Allow accessing UMD globals from modules. */\n\t\t/* Experimental Options */\n\t\t// \"experimentalDecorators\": true,              /* Enables experimental support for ES7 decorators. */\n\t\t// \"emitDecoratorMetadata\": true,               /* Enables experimental support for emitting type metadata for decorators. */\n\t\t/* Advanced Options */\n\t\t\"skipLibCheck\": true, /* Skip type checking of declaration files. */\n\t\t\"forceConsistentCasingInFileNames\": true, /* Disallow inconsistently-cased references to the same file. */\n    \"noUncheckedIndexedAccess\": true\n\t},\n\t\"exclude\": [\n\t\t\"node_modules\",\n\t\t\".build\"\n\t]\n}"
  }
]