├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── LICENSE ├── README.md ├── docusaurus ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ └── api │ │ ├── _category_.json │ │ ├── booleans.md │ │ ├── core.md │ │ ├── functions.md │ │ ├── numbers.md │ │ ├── objects.md │ │ ├── strings.md │ │ ├── tuples.md │ │ └── unions.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.module.css │ │ ├── index.tsx │ │ └── markdown-page.md ├── static │ ├── .nojekyll │ └── img │ │ ├── docusaurus.png │ │ ├── favicon.ico │ │ └── logo.svg └── tsconfig.json ├── jest.config.cjs ├── package-lock.json ├── package.json ├── src ├── index.ts └── internals │ ├── booleans │ └── Booleans.ts │ ├── core │ ├── Core.ts │ └── impl │ │ └── MergeArgs.ts │ ├── functions │ └── Functions.ts │ ├── helpers.ts │ ├── match │ ├── Match.ts │ └── impl │ │ └── match.ts │ ├── numbers │ ├── Numbers.ts │ └── impl │ │ ├── absolute.ts │ │ ├── addition.ts │ │ ├── compare.ts │ │ ├── digits │ │ ├── addition.ts │ │ ├── division.ts │ │ ├── multiply.ts │ │ ├── power.ts │ │ └── substraction.ts │ │ ├── division.ts │ │ ├── multiply.ts │ │ ├── negate.ts │ │ ├── numbers.ts │ │ ├── power.ts │ │ ├── range.ts │ │ ├── substraction.ts │ │ └── utils.ts │ ├── objects │ ├── Objects.ts │ └── impl │ │ └── objects.ts │ ├── std │ └── Std.ts │ ├── strings │ ├── Strings.ts │ └── impl │ │ ├── compare.ts │ │ ├── length.ts │ │ ├── repeat.ts │ │ ├── replace.ts │ │ ├── split.ts │ │ ├── strings.ts │ │ ├── trim.ts │ │ └── utils.ts │ ├── tuples │ └── Tuples.ts │ └── unions │ └── Unions.ts ├── test ├── booleans.test.ts ├── core.test.ts ├── functions.test.ts ├── match.test.ts ├── numbers.test.ts ├── objects.test.ts ├── real-world │ └── reselect.test.ts ├── strings.test.ts ├── tsconfig.json ├── tuples.test.ts └── unions.test.ts └── tsconfig.json /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | name: Node.js 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | matrix: 13 | node-version: [18.x] 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | cache: "npm" 22 | - run: npm install 23 | - run: npm run test 24 | - run: npm run build 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | lib 5 | dist 6 | notes.md 7 | .vscode/ 8 | tracing_output_folder/ 9 | *.tgz -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Gabriel Vergnaud 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Higher-Order TypeScript (HOTScript) 2 | 3 | A library of composable functions for the type level! 4 | 5 | Transform your TypeScript types in any way you want using functions you already know. 6 | 7 | ![image](https://user-images.githubusercontent.com/9265418/223854503-b54f6a62-9f21-4953-aaa3-5d54699516a7.png) 8 | 9 | ## Features 10 | 11 | - Type-level higher-order functions (`Tuples.Map`, `Tuples.Filter`, `Objects.MapValues`, etc). 12 | - Type-level pattern matching with `Match`. 13 | - Performant math operations (`Numbers.Add`, `Numbers.Sub`, `Numbers.Mul`, `Numbers.Div`, etc). 14 | - Custom "lambda" functions. 15 | 16 | 🚧 work in progress 🚧 17 | 18 | ## Installation 19 | 20 | You can find HotScript on npm: 21 | 22 | ```ts 23 | npm install -D hotscript 24 | ``` 25 | 26 | HotScript is a work-in-progress library, so expect **breaking changes** in its API. 27 | 28 | ## Examples 29 | 30 | #### Transforming a list 31 | 32 | [Run this as a TypeScript Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgBWGApgGjgFQK5gA26AztgMoxTAB2A5mXAHJ4gBG6UJcAvnAGZQIIOACIAFhBgkAxtTAxRAbgBQKmAE8McKKQCMcALwo06ADwq4cAPTWrAPQD8cAGwAmS3ADae7G+wAzNgALAC6mJ5enlb4RKQAdACyAIZgZizsnCTxAIIAJnlmAQB8xdhWFTZ2XsHYAKzYLtgA7KHRuATE2QBSELRmovGiZZWjY1a2YsHxdfEu8c2i7ZTU9NnkRMAwA0Mj4+OTXqLBotiidadiLpeii22VsV1JqWYrtAzxyLoYNIWiesMRod-icznoLmDrmC7u1HgkUmk3mt4jgIBkOFA9hVDnpanBwdg9E18a1YZ0EuRWPtqQc7O5PG1ikogA) 33 | 34 | 35 | ```ts 36 | import { Pipe, Tuples, Strings, Numbers } from "hotscript"; 37 | 38 | type res1 = Pipe< 39 | // ^? 62 40 | [1, 2, 3, 4], 41 | [ 42 | Tuples.Map>, // [4, 5, 6, 7] 43 | Tuples.Join<".">, // "4.5.6.7" 44 | Strings.Split<".">, // ["4", "5", "6", "7"] 45 | Tuples.Map>, // ["14", "15", "16", "17"] 46 | Tuples.Map, // [14, 15, 16, 17] 47 | Tuples.Sum // 62 48 | ] 49 | >; 50 | ``` 51 | 52 | #### Defining a first-class function 53 | 54 | [Run this as a TypeScript Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgYQIYBt0Bo4DEB2OAKgK5joCmAznAL5wBmUEIcARABYQxUDGUwMDDYBuAFBiA9JLhEOwGgrio4MAJ5gKAWkoA3CunbpUIAEYATVGwCEY4PhgUoDVLwpwAImXTBeqR3AUAB6O+OY0BIhicHBQFDAkUPgAXHAA2jDyVGlsqFAA5gAMbAC6OJkKOXlFpSXitBLqmrHUJOgwAIxwALwoGOgAPKTk1AB0ALKoYANe5L7+FAB8OGkdOABMOADMOAAsJYvi0jEnAHoA-I0a7nFUbTDrPX2YQ95juMYwk9OzPn6Oy3SazgmzgOzg+0OUhkJzgFyAA) 55 | 56 | ```ts 57 | import { Call, Fn, Tuples } from "hotscript"; 58 | 59 | // This is a type-level "lambda"! 60 | interface Duplicate extends Fn { 61 | return: [this["arg0"], this["arg0"]]; 62 | } 63 | 64 | type result1 = Call, [1, 2, 3, 4]>; 65 | // ^? [[1, 1], [2, 2], [3, 3], [4, 4]] 66 | 67 | type result2 = Call, [1, 2, 3, 4]>; 68 | // ^? [1, 1, 2, 2, 3, 3, 4, 4] 69 | ``` 70 | 71 | #### Transforming an object type 72 | 73 | [Run this as a TypeScript Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgBWGApgGjgeQEYBW6AxjAM7YBCEEANugIYB2ZcAvnAGZQQhwBEACwjliUNDH4BuAFAyA9PLgAZdDADkrYr0hl0cMr32cArk1LAILODAg2ozMp2h9mcCIRLwYATwwAuGV8MOAAVCABBZABJZAYfWggGABMAHlCAPjgAXhQ0dFSZODDMIrgAbTLi-CJSMgA6HBBgGEofVOo6RhZ6gFEARxMGWjJUsh8QPDoMjNLi6s86+oiyMmAAcyZUpBA1FIYYBn9EOCZ0AHcAVT0oY5goE30ONlmq3EXyeoBlJgYAa3QAGEGHoACLodBgObzGpeBorNabbZwYDJY5ke7AJjrdgZMoAXRkGVkwX0UWi13QUByYUiMTiCSSaQQZUU8wAegB+Mqo9ETKa0WTFTjAKAYgByDF26Mx2KFcFoIJgkulBll61kL1kbIASmoTFAWIFSWEaSziry1eI5WVdodkgcjiczucAPomG53B5PeUisUwV2-VUY60asqKjGBqXoGWhzVSIA) 74 | 75 | ```ts 76 | import { Pipe, Objects, Booleans } from "hotscript"; 77 | 78 | // Let's compose some functions to transform an object type: 79 | type ToAPIPayload = Pipe< 80 | T, 81 | [ 82 | Objects.OmitBy>, 83 | Objects.Assign<{ metadata: { newUser: true } }>, 84 | Objects.SnakeCaseDeep, 85 | Objects.Assign<{ id: string }> 86 | ] 87 | >; 88 | type T = ToAPIPayload<{ 89 | id: symbol; 90 | firstName: string; 91 | lastName: string; 92 | }>; 93 | // Returns: 94 | type T = { 95 | id: string; 96 | metadata: { new_user: true }; 97 | first_name: string; 98 | last_name: string; 99 | }; 100 | ``` 101 | 102 | #### Parsing a route path 103 | 104 | [Run this as a TypeScript Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgBWGApgGjgeQEYBW6AxjAM7YDKMUwAdgOYVwDCE4EZ6AMugGYxsAFQCuYADbpmAWQCGMYgAs4AXzh8o7OACJFEcsVpgY2gNwAoczACeGOFCkBWOAF4UadAB5zcOAHo-X18APQB+Hx0-ES4oMj9PYAATAC4yGnoGAD4-SDS4hLpE9AAPZLoREDx0KEztTAiAbQjfalpGMgA6SglgGE9tP21M+qC4UQkpDoAxYHEYas9WjM7qWVgyAHVexX7+zOHmsbFJTrkwb1HfNg4uXgFPJsugpfaOoVoQXe04AB8dWoOTxa6Ve3XEvX6ySGI0uAF1ModAUFxic3hAAKp0YAQOgw3z4IikTpTTQgACidHSUjxuEIJHIHTOADVZOIRFILpc5Aodo8gdylB0tjAdto0m0GHU4OKMkingLFELtv1ypVqlLVVUaodfPDERFYeZMqYgA) 105 | 106 | https://user-images.githubusercontent.com/2315749/222081717-96217cd2-ac89-4e06-a942-17fbda717cd2.mp4 107 | 108 | ```ts 109 | import { Pipe, Objects, Strings, ComposeLeft, Tuples, Match } from "hotscript"; 110 | 111 | type res5 = Pipe< 112 | // ^? { id: string, index: number } 113 | "/users//posts/", 114 | [ 115 | Strings.Split<"/">, 116 | Tuples.Filter>, 117 | Tuples.Map">, Strings.Split<":">]>>, 118 | Tuples.ToUnion, 119 | Objects.FromEntries, 120 | Objects.MapValues< 121 | Match<[Match.With<"string", string>, Match.With<"number", number>]> 122 | > 123 | ] 124 | >; 125 | ``` 126 | ### Make querySelector typesafe 127 | 128 | [Run this as a TypeScript Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAKjgQwM5wBJwGZQiOAcgAsIZUBjKYMGQgKHoBMBTCgG2ShZwFcA7CjGAR+cAI68WUAJ4BlFuzYxoAHgAqcFgA8YLfk3SoY1fgHMAfAApUi5dABccdQEonAUSUh9MAGJ4QBSUhNXULOAAfOH5ednZGYH49KGxkCh51agIdPQN0DAA6XzEAb3o4CrhuGF4ofgdyyphiYFQAbQAiLjMABg6AXS1dfUM4AAMAEhLE7Gk4AAVuADcAXzgAGimZuYA5HJWxuAB+RorCiY0s9fGpxZZVzZK93QPwhsq4ZtbO7r7BnJG6Em034sygC2WK2uW1Bu32hxOH3Ol1A12BdweU2eMFecHeTRa7S6UF6AyGuVGwO24IxKwcrSsMLBcE8KxcTLmACUWMYDsdTpgChdMqibiVaVNWVNubyxm8BV8ib8yQC8mLqRD7nSAO7EaQsRkg5ms9lGrk8nEIgXIkUgNG3SGS9graUW3H4iqKn4kv7kwHq2Hg1mG4ymMxsjngmWW-lIoUou1iqUlaPuhWE72k-7DNVUwMs51tKahxLh-qRuCpq1x4VXJPO12y+UfL3ErP0FaMGAyMA8TwsbxJfz4IL2KAacIAXkF8xoLA01zaAtt62tBTkJlLqHXYHYwBgqkIRAsq7j6l4u55BQAMmgYKfKoUN2Ht3Jd-vD+tCCe1+fL9v1AgABVfgRH4B8znXTdzFfd8Dw6BwOkiOAOk6ZCOgKJCog6ABiDofzPC8lG3ABBA8egIx8CgAWWQGAKGIVQlw+ONaPo4gCgAdX3RiAGsWBkCBsEwdRqOvftBxgdRkDMHZkG8WiwGuQoAHkACMACtlG3ABxFgD0KAB9ZTRPErwfGk2T5JYRSLEoljBTYhiuJ41RkH4GQTLEiSfAsAV+j8gLGHoChRGMKoeR6OBp0kaR5DsEIoCsDoEA6FwAG56AAeiyliAD0TlC-hwu4VAAEZookKRZFHRLkrANLMpy-LCrC+BSoAJkq2KaoSlQko6JhgCWAoODQVA5O8Rrstyj4CpCtqItQABmbrqvi4J+uSoaljgcJkGm5q5ta4r2p5AAWNa4tqraOjANo6JMfpDtmyp5qKkqeQAViu3rNugbbhrgMACj0XQUDaYhuGwSdCBwwhnoymaWoW06loANl+jax2SnDgCYF6UY+s7UAAdixm6AcG4aHpgJ6HGINArDAPa4FDUQzBcQnjtRz7UAADgpvqqfpVArB22mnuuMAuaRo63pOvmAE4hf+gaJceqB+mlyWtYZpmWfCdnzFlprXoqd7FtKsqopi9bKfVoG3kZsXkFZ0WrDU64KBcWXkZ54L6CAA) 129 | ```ts 130 | import * as H from 'hotscript' 131 | 132 | declare function querySelector(selector: T): ElementFromSelector | null 133 | 134 | interface Trim extends H.Fn { 135 | return: 136 | this["arg0"] extends `${infer Prev} ,${infer Next}` ? 137 | H.$ : 138 | this["arg0"] extends `${infer Prev}, ${infer Next}` ? 139 | H.$ : 140 | this["arg0"] extends `${infer Prev}:is(${infer El})${infer Rest}` ? 141 | H.$ : 142 | this["arg0"] extends `${infer Prev}:where(${infer El})${infer Rest}` ? 143 | H.$ : 144 | this["arg0"] extends `${infer El}(${string})${infer Rest}` ? 145 | H.$ : 146 | this["arg0"] extends `${infer El}[${string}]${infer Rest}` ? 147 | H.$ : 148 | this["arg0"] 149 | } 150 | 151 | type ElementFromSelector = H.Pipe, 154 | H.Tuples.Last, 155 | H.Strings.Split<','>, 156 | H.Tuples.ToUnion, 157 | H.Strings.Split<":" | "[" | "." | "#">, 158 | H.Tuples.At<0>, 159 | H.Match<[ 160 | H.Match.With>, 161 | H.Match.With 162 | ]> 163 | ]> 164 | ``` 165 | ![image](https://github.com/gvergnaud/hotscript/assets/633115/d75fe58e-e677-4ead-9cd2-b82a32d0cec7) 166 | 167 | 168 | ## API 169 | 170 | - [x] Core 171 | - [x] `Pipe`: Pipes a type through several functions. 172 | - [x] `PipeRight`: Pipe a type from right to left. 173 | - [x] `Call`: Call a type level `Fn` function. 174 | - [x] `Apply`: Apply several arguments to an `Fn` function. 175 | - [x] `PartialApply`: Make an `Fn` partially applicable. 176 | - [x] `Compose`: Compose `Fn` functions from right to left. 177 | - [x] `ComposeLeft`: Compose `Fn` functions from left to right. 178 | - [x] `args`, `arg0`, `arg1`, `arg2`, `arg3`: Access piped parameters (Useful in combination with `Objects.Create`). 179 | - [x] `_`: Placeholder to partially apply any built-in functions, or functions created with `PartialApply`. 180 | - [x] Function 181 | - [x] `ReturnType`: Extract the return type from a function type. 182 | - [x] `Parameters`: Extract the parameters from a function type as a tuple. 183 | - [x] `Parameter`: Extract the parameter at index `N` from a function type. 184 | - [x] `MapReturnType`: Transform the return type of a function type using an `Fn`. 185 | - [x] `MapParameters`: Transform the tuple of parameters of a function type using an `Fn`. 186 | - [x] Tuples 187 | - [x] `Create -> [X]`: Create a unary tuple from a type. 188 | - [x] `Partition`: Using a predicate `Fn`, turn a list of types into two lists `[Passing[], Rejected[]]`. 189 | - [x] `IsEmpty`: Check if a tuple is empty. 190 | - [x] `Zip<...Tuple[]>`: Zips several tuples together. For example. it would turn `[[a,b,c], [1,2,3]]` into `[[a, 1], [b, 2], [c, 3]]`. 191 | - [x] `ZipWith`: Zip several tuples by calling a zipper `Fn` with one argument per input tuple. 192 | - [x] `Sort`: Sorts a tuple of number literals. 193 | - [x] `Head`: Returns the first element from a tuple type. 194 | - [x] `Tail`: Drops the first element from a tuple type. 195 | - [x] `At`: Returns the `N`th element from a tuple. 196 | - [x] `Last`: Returns the last element from a tuple. 197 | - [x] `FlatMap`: Calls an `Fn` function returning a tuple on each element of the input tuple, and flattens all of the returned tuples into a single one. 198 | - [x] `Find`: Finds an element from a tuple using a predicate `Fn`. 199 | - [x] `Drop`: Drops the `N` first elements from a tuple. 200 | - [x] `Take`: Takes the `N` first elements from a tuple. 201 | - [x] `TakeWhile`: Take elements while the `Fn` predicate returns `true`. 202 | - [x] `GroupBy`: Transform a list into an object containing lists. The `Fn` function takes each element and returns the key it should be added to. 203 | - [x] `Join`: Joins several strings together using the `Str` separator string. 204 | - [x] `Map`: Transforms each element in a tuple. 205 | - [x] `Filter`: Removes elements from a tuple if the `Fn` predicate function doesn't return `true`. 206 | - [x] `Reduce`: Iterates over a tuple a reduce it to a single function using a reducer `Fn`. 207 | - [x] `ReduceRight`: like `Reduce`, but starting from the end of the list. 208 | - [x] `Reverse`: Reverses the tuple. 209 | - [x] `Every`: Checks if all element passes the `Fn` predicate. 210 | - [x] `Some`: Checks if at least one element passes the `Fn` predicate. 211 | - [x] `SplitAt`: Split a tuple into a left and a right tuple using an index. 212 | - [x] `ToUnion`: Turns a tuple into a union of elements. 213 | - [x] `ToIntersection`: Turns a tuple into an intersection of elements. 214 | - [x] `Prepend`: Adds a type at the beginning of a tuple. 215 | - [x] `Append`: Adds a type at the end of a tuple. 216 | - [x] `Concat`: Merges two tuples together. 217 | - [x] `Min`: Returns the minimum number in a list of number literal types. 218 | - [x] `Max`: Returns the maximum number in a list of number literal types. 219 | - [x] `Sum`: Add all numbers in a list of number literal types together. 220 | - [x] Object 221 | - [x] `Readonly`: Makes all object keys `readonly`. 222 | - [x] `Mutable`: Removes `readonly` from all object keys. 223 | - [x] `Required`: Makes all keys required. 224 | - [x] `Partial`: Makes all keys optional. 225 | - [x] `ReadonlyDeep`: Recursively makes all object keys `readonly`. 226 | - [x] `MutableDeep`: Recursively removes `readonly` from all object keys. 227 | - [x] `RequiredDeep`: Recursively makes all keys required. 228 | - [x] `PartialDeep`: Recursively makes all keys optional. 229 | - [x] `Update`: Immutably update an object's field under a certain path. Paths are dot-separated strings: `a.b.c`. 230 | - [x] `Record`: Creates an object type with keys of type `Key` and values of type `Value`. 231 | - [x] `Keys`: Extracts the keys from an object type `Obj`. 232 | - [x] `Values`: Extracts the values from an object type `Obj`. 233 | - [x] `AllPaths`: Extracts all possible paths of an object type `Obj`. 234 | - [x] `Create`: Creates an object of type Pattern with values of type X. 235 | - [x] `Get`: Gets the value at the specified path `Path` in the object `Obj`. 236 | - [x] `FromEntries<[Key, Value]>`: Creates an object from a union of key-value pairs. 237 | - [x] `Entries`: Extracts the union of key-value pairs from an object type `Obj`. 238 | - [x] `MapValues`: Transforms the values of an object type `Obj` using a mapper function `Fn`. 239 | - [x] `MapKeys`: Transforms the keys of an object type `Obj` using a mapper function `Fn`. 240 | - [x] `Assign<...Obj>`: Merges multiple objects together. 241 | - [x] `Pick`: Picks specific keys `Key` from an object type `Obj`. 242 | - [x] `PickBy`: Picks keys from an object type `Obj` based on a predicate function `Fn`. 243 | - [x] `Omit`: Omits specific keys `Key` from an object type `Obj`. 244 | - [x] `OmitBy`: Omits keys from an object type `Obj` based on a predicate function `Fn`. 245 | - [x] `CamelCase`: Converts the keys of an object type `Obj` to camelCase. 246 | - [x] `CamelCaseDeep`: Recursively converts the keys of an object type `Obj` to camelCase. 247 | - [x] `SnakeCase`: Converts the keys of an object type `Obj` to snake_case. 248 | - [x] `SnakeCaseDeep`: Recursively converts the keys of an object `type` Obj to snake_case. 249 | - [x] `KebabCase`: Converts the keys of an object type `Obj` to kebab-case. 250 | - [x] `KebabCaseDeep`: Recursively converts the keys of an object type Obj to kebab-case. 251 | - [x] Union 252 | - [x] `Map`: Transforms each member of a union type `U` using a mapper function `Fn`. 253 | - [x] `Extract`: Extracts the subset of a union type `U` that is assignable to type `T`. 254 | - [x] `ExtractBy`: Extracts the subset of a union type`U`that satisfies the predicate function `Fn`. 255 | - [x] `Exclude`: Excludes the subset of a union type`U`that is assignable to type `T`. 256 | - [x] `ExcludeBy`: Excludes the subset of a union type`U`that satisfies the predicate function `Fn`. 257 | - [x] `NonNullable`: Removes null and undefined from a union type `U`. 258 | - [x] `ToTuple`: Converts a union type`U`to a tuple type. 259 | - [x] `ToIntersection`: Converts a union type`U`to an intersection type. 260 | - [x] String 261 | - [x] `Length`: Returns the length of a string type `Str`. 262 | - [x] `TrimLeft`: Removes the specified character from the left side of a string type `Str`. 263 | - [x] `TrimRight`: Removes the specified character from the right side of a string type `Str`. 264 | - [x] `Trim`: Removes the specified character from both sides of a string type `Str`. 265 | - [x] `Join`: Joins multiple string type `Str` with a separator `Sep`. 266 | - [x] `Replace`: Replaces all occurrences of a substring `From` with another substring `To` in a string type `Str`. 267 | - [x] `Slice`: Extracts a portion of a string type `Str` from index `Start` to index `End`. 268 | - [x] `Split`: Splits a string type `Str` into a tuple of substrings using a separator `Sep`. 269 | - [x] `Repeat`: Repeats a string type `Str` `N` times. 270 | - [x] `StartsWith`: Checks if a string type `Str` starts with a substring `S`. 271 | - [x] `EndsWith`: Checks if a string type `Str` ends with a substring `E`. 272 | - [x] `ToTuple`: Converts a string type `Str` to a tuple type. 273 | - [x] `ToNumber`: Converts a string type `Str` to a number type. 274 | - [x] `ToString`: Converts any literal type `T` to a string literal type. 275 | - [x] `Prepend`: Prepends a string type `Start` to the beginning of a string type `Str`. 276 | - [x] `Append`: Appends a string type `End` to the end of a string type `Str`. 277 | - [x] `Uppercase`: Converts a string type `Str` to uppercase. 278 | - [x] `Lowercase`: Converts a string type `Str` to lowercase. 279 | - [x] `Capitalize`: Capitalizes the first letter of a string type `Str`. 280 | - [x] `Uncapitalize`: Converts the first letter of a string type `Str` to lowercase. 281 | - [x] `SnakeCase`: Converts a string type `Str` to snake_case. 282 | - [x] `CamelCase`: Converts a string type `Str` to camelCase. 283 | - [x] `KebabCase`: Converts a string type `Str` to kebab-case. 284 | - [x] `Compare`: Compares two string types `Str1` and `Str2` and returns a number indicating their relative order. 285 | - [x] `Equal`: Checks if two string types `Str1` and `Str2` are equal. 286 | - [x] `NotEqual`: Checks if two string types `Str1` and `Str2` are not equal. 287 | - [x] `LessThan`: Checks if `Str1` is less than `Str2` in lexicographical order. 288 | - [x] `LessThanOrEqual`: Checks if `Str1` is less than or equal to `Str2` in lexicographical order. 289 | - [x] `GreaterThan`: Checks if `Str1` is greater than `Str2` in lexicographical order. 290 | - [x] `GreaterThanOrEqual`: Checks if `Str1` is greater than or equal to `Str2` in lexicographical order. 291 | - [x] Number 292 | - [x] `Add`: Adds two number types `N` and `M`. 293 | - [x] `Multiply`: Multiplies two number types `N` and `M`. 294 | - [x] `Subtract`: Subtracts the number type `M` from `N`. 295 | - [x] `Negate`: Negates a number type `N` by changing its sign. 296 | - [x] `Power`: Raises a number type `N` to the power of `M`. 297 | - [x] `Div`: Divides a number type `N` by `M`. 298 | - [x] `Mod`: Calculates the remainder of dividing a number type `N` by `M`. 299 | - [x] `Abs`: Returns the absolute value of a number type `N`. 300 | - [x] `Compare`: Compares two number types `N` and `M` and returns a number indicating their relative order. 301 | - [x] `GreaterThan`: Checks if the number type `N` is greater than `M`. 302 | - [x] `GreaterThanOrEqual`: Checks if the number type `N` is greater than or equal to `M`. 303 | - [x] `LessThan`: Checks if the number type `N` is less than `M`. 304 | - [x] `LessThanOrEqual`: Checks if the number type `N` is less than or equal to `M`. 305 | - [x] Boolean 306 | - [x] `And`: Performs a logical AND operation between two boolean types `Bool1` and `Bool2`. 307 | - [x] `Or`: Performs a logical OR operation between two boolean types `Bool1` and `Bool2`. 308 | - [x] `XOr`: Performs a logical XOR (exclusive OR) operation between two boolean types `Bool1` and `Bool2`. 309 | - [x] `Not`: Performs a logical NOT operation on a boolean type `Bool`. 310 | - [x] `Extends`: Checks if type `A` extends or is equal to type `B`. 311 | - [x] `Equals`: Checks if type `A` is equal to type `B`. 312 | - [x] `DoesNotExtend`: Checks if type `A` does not extend type `B`. 313 | -------------------------------------------------------------------------------- /docusaurus/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docusaurus/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docusaurus/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docusaurus/docs/api/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "API", 3 | "position": 2, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "HOTScript's API exports" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docusaurus/docs/api/booleans.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Booleans 6 | 7 | ## Import 8 | 9 | ```ts 10 | import { Booleans } from "hotscript"; 11 | // or 12 | import { B } from "hotscript"; 13 | ``` 14 | -------------------------------------------------------------------------------- /docusaurus/docs/api/core.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | --- 4 | 5 | # Core 6 | 7 | ## `Fn` 8 | 9 | ## `Pipe` 10 | 11 | ## `PipeRight` 12 | 13 | ## `Apply` 14 | 15 | ## `Call` 16 | -------------------------------------------------------------------------------- /docusaurus/docs/api/functions.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Functions 6 | 7 | ## Import 8 | 9 | ```ts 10 | import { Functions } from "hotscript"; 11 | // or 12 | import { F } from "hotscript"; 13 | ``` 14 | -------------------------------------------------------------------------------- /docusaurus/docs/api/numbers.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Numbers 6 | 7 | ## Import 8 | 9 | ```ts 10 | import { Numbers } from "hotscript"; 11 | // or 12 | import { N } from "hotscript"; 13 | ``` 14 | -------------------------------------------------------------------------------- /docusaurus/docs/api/objects.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Objects 6 | 7 | ## Import 8 | 9 | ```ts 10 | import { Objects } from "hotscript"; 11 | // or 12 | import { O } from "hotscript"; 13 | ``` 14 | -------------------------------------------------------------------------------- /docusaurus/docs/api/strings.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Strings 6 | 7 | ## Import 8 | 9 | ```ts 10 | import { Strings } from "hotscript"; 11 | // or 12 | import { S } from "hotscript"; 13 | ``` 14 | -------------------------------------------------------------------------------- /docusaurus/docs/api/tuples.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Tuples 6 | 7 | ## Import 8 | 9 | ```ts 10 | import { Tuples } from "hotscript"; 11 | // or 12 | import { T } from "hotscript"; 13 | ``` 14 | -------------------------------------------------------------------------------- /docusaurus/docs/api/unions.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Unions 6 | 7 | ## Import 8 | 9 | ```ts 10 | import { Unions } from "hotscript"; 11 | // or 12 | import { U } from "hotscript"; 13 | ``` 14 | -------------------------------------------------------------------------------- /docusaurus/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const lightCodeTheme = require('prism-react-renderer/themes/github'); 5 | const darkCodeTheme = require('prism-react-renderer/themes/dracula'); 6 | 7 | /** @type {import('@docusaurus/types').Config} */ 8 | const config = { 9 | title: 'Higher-Order Typescript', 10 | tagline: 'A lodash-like library for types with support for type-level lambda functions.', 11 | favicon: 'img/favicon.ico', 12 | 13 | // Set the production url of your site here 14 | url: 'https://hotscript.dev', 15 | // Set the // pathname under which your site is served 16 | // For GitHub pages deployment, it is often '//' 17 | baseUrl: '/', 18 | 19 | // GitHub pages deployment config. 20 | // If you aren't using GitHub pages, you don't need these. 21 | organizationName: 'gvergnaud', // Usually your GitHub org/user name. 22 | projectName: 'HOTScript', // Usually your repo name. 23 | 24 | onBrokenLinks: 'throw', 25 | onBrokenMarkdownLinks: 'warn', 26 | 27 | // Even if you don't use internalization, you can use this field to set useful 28 | // metadata like html lang. For example, if your site is Chinese, you may want 29 | // to replace "en" with "zh-Hans". 30 | i18n: { 31 | defaultLocale: 'en', 32 | locales: ['en'], 33 | }, 34 | 35 | presets: [ 36 | [ 37 | 'classic', 38 | /** @type {import('@docusaurus/preset-classic').Options} */ 39 | ({ 40 | docs: { 41 | sidebarPath: require.resolve('./sidebars.js'), 42 | editUrl: 43 | 'https://github.com/gvergnaud/HOTScript/tree/main/docusaurus/', 44 | }, 45 | theme: { 46 | customCss: require.resolve('./src/css/custom.css'), 47 | }, 48 | }), 49 | ], 50 | ], 51 | 52 | themeConfig: 53 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 54 | ({ 55 | // Replace with your project's social card 56 | image: 'img/docusaurus-social-card.jpg', 57 | navbar: { 58 | title: 'HOTScript', 59 | logo: { 60 | alt: 'HOTScript Logo', 61 | src: 'img/logo.svg', 62 | }, 63 | items: [ 64 | { 65 | to: 'docs/category/api', 66 | position: 'left', 67 | label: 'API', 68 | }, 69 | { 70 | href: 'https://github.com/gvergnaud/HOTScript', 71 | label: 'GitHub', 72 | position: 'right', 73 | }, 74 | ], 75 | }, 76 | footer: { 77 | style: 'dark', 78 | links: [ 79 | { 80 | title: 'Resources', 81 | items: [ 82 | { 83 | label: 'API', 84 | to: '/docs/category/api', 85 | }, 86 | ], 87 | }, 88 | { 89 | title: 'Community', 90 | items: [ 91 | { 92 | label: 'GitHub', 93 | href: 'https://github.com/gvergnaud/HOTScript', 94 | }, 95 | { 96 | label: 'Discord', 97 | // todo(adamsuskin): get discord server link 98 | href: 'https://github.com/gvergnaud/HOTScript', 99 | }, 100 | { 101 | label: 'Twitter', 102 | href: 'https://twitter.com/GabrielVergnaud', 103 | }, 104 | ], 105 | }, 106 | ], 107 | copyright: `Copyright © ${new Date().getFullYear()}. Created by Gabriel Vergnaud. Built with Docusaurus.`, 108 | }, 109 | prism: { 110 | theme: lightCodeTheme, 111 | darkTheme: darkCodeTheme, 112 | }, 113 | }), 114 | }; 115 | 116 | module.exports = config; 117 | -------------------------------------------------------------------------------- /docusaurus/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc" 16 | }, 17 | "dependencies": { 18 | "@docusaurus/core": "2.3.1", 19 | "@docusaurus/preset-classic": "2.3.1", 20 | "@mdx-js/react": "^1.6.22", 21 | "clsx": "^1.2.1", 22 | "prism-react-renderer": "^1.3.5", 23 | "react": "^17.0.2", 24 | "react-dom": "^17.0.2" 25 | }, 26 | "devDependencies": { 27 | "@docusaurus/module-type-aliases": "2.3.1", 28 | "@tsconfig/docusaurus": "^1.0.5", 29 | "typescript": "^4.7.4" 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.5%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | }, 43 | "engines": { 44 | "node": ">=16.14" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /docusaurus/sidebars.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 4 | const sidebars = { 5 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 6 | }; 7 | 8 | module.exports = sidebars; 9 | -------------------------------------------------------------------------------- /docusaurus/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /docusaurus/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /docusaurus/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 4 | import Layout from '@theme/Layout'; 5 | 6 | import styles from './index.module.css'; 7 | 8 | function HomepageHeader() { 9 | const {siteConfig} = useDocusaurusContext(); 10 | return ( 11 |
12 |
13 |

{siteConfig.title}

14 |

{siteConfig.tagline}

15 |
16 |
17 | ); 18 | } 19 | 20 | export default function Home(): JSX.Element { 21 | const {siteConfig} = useDocusaurusContext(); 22 | return ( 23 | 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /docusaurus/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /docusaurus/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gvergnaud/hotscript/0bc205286bd5eea0b89fa903c411df9aca95923c/docusaurus/static/.nojekyll -------------------------------------------------------------------------------- /docusaurus/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gvergnaud/hotscript/0bc205286bd5eea0b89fa903c411df9aca95923c/docusaurus/static/img/docusaurus.png -------------------------------------------------------------------------------- /docusaurus/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gvergnaud/hotscript/0bc205286bd5eea0b89fa903c411df9aca95923c/docusaurus/static/img/favicon.ico -------------------------------------------------------------------------------- /docusaurus/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docusaurus/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@tsconfig/docusaurus/tsconfig.json", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /jest.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testEnvironment: "node", 4 | }; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hotscript", 3 | "version": "1.0.13", 4 | "description": "A library of composable functions for the type-level! Transform your TypeScript types in any way you want using functions you already know.", 5 | "main": "./dist/index.cjs", 6 | "module": "./dist/index.js", 7 | "types": "dist/index.d.ts", 8 | "scripts": { 9 | "build": "tsup src/index.ts --format esm,cjs && tsc src/index.ts --declaration --emitDeclarationOnly --outDir dist", 10 | "prepublishOnly": "npm run test && npm run build", 11 | "test": "jest", 12 | "clear-test": "jest --clearCache", 13 | "perf": "tsc --project test/tsconfig.json --noEmit --extendedDiagnostics", 14 | "fmt": "prettier ./src/** ./test/** -w", 15 | "check": "tsc --strict --noEmit --extendedDiagnostics" 16 | }, 17 | "files": [ 18 | "dist/**/*" 19 | ], 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/gvergnaud/HOTScript.git" 23 | }, 24 | "license": "ISC", 25 | "bugs": { 26 | "url": "https://github.com/gvergnaud/HOTScript/issues" 27 | }, 28 | "homepage": "https://github.com/gvergnaud/HOTScript#readme", 29 | "author": "Gabriel Vergnaud", 30 | "devDependencies": { 31 | "@types/jest": "^29.4.0", 32 | "jest": "^29.4.2", 33 | "prettier": "^2.8.4", 34 | "ts-jest": "^29.0.5", 35 | "tsup": "^6.7.0", 36 | "typescript": "^4.9.5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Apply, 3 | Call, 4 | Fn, 5 | Pipe, 6 | PipeRight, 7 | Compose, 8 | ComposeLeft, 9 | Constant, 10 | Identity, 11 | PartialApply, 12 | _, 13 | arg, 14 | arg0, 15 | arg1, 16 | arg2, 17 | arg3, 18 | args, 19 | } from "./internals/core/Core"; 20 | import { Functions } from "./internals/functions/Functions"; 21 | import { Numbers } from "./internals/numbers/Numbers"; 22 | import { Objects } from "./internals/objects/Objects"; 23 | import { Strings } from "./internals/strings/Strings"; 24 | import { Tuples } from "./internals/tuples/Tuples"; 25 | import { Unions } from "./internals/unions/Unions"; 26 | import { Booleans } from "./internals/booleans/Booleans"; 27 | import { Match } from "./internals/match/Match"; 28 | 29 | export { 30 | _, 31 | arg, 32 | arg0, 33 | arg1, 34 | arg2, 35 | arg3, 36 | args, 37 | Fn, 38 | Pipe, 39 | PipeRight, 40 | Call, 41 | Call as $, 42 | Apply, 43 | Compose, 44 | ComposeLeft, 45 | Constant, 46 | Identity, 47 | PartialApply, 48 | Match, 49 | Booleans, 50 | Objects, 51 | Unions, 52 | Strings, 53 | Numbers, 54 | Tuples, 55 | Functions, 56 | Booleans as B, 57 | Objects as O, 58 | Unions as U, 59 | Strings as S, 60 | Numbers as N, 61 | Tuples as T, 62 | Functions as F, 63 | }; 64 | -------------------------------------------------------------------------------- /src/internals/booleans/Booleans.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Every, Some } from "../helpers"; 2 | import { Compose, Fn, PartialApply, unset } from "../core/Core"; 3 | 4 | export namespace Booleans { 5 | type ExtendsImpl = [a] extends [b] ? true : false; 6 | 7 | interface ExtendsFn extends Fn { 8 | return: this["args"] extends [infer first, infer second, ...any] 9 | ? ExtendsImpl 10 | : never; 11 | } 12 | 13 | export type Extends = PartialApply< 14 | ExtendsFn, 15 | b extends unset ? [unset, a] : [a, b] 16 | >; 17 | 18 | type NotImpl = a extends true ? false : true; 19 | 20 | interface NotFn extends Fn { 21 | return: this["args"] extends [infer first, ...any] ? NotImpl : never; 22 | } 23 | 24 | export type Not = PartialApply; 25 | 26 | interface EqualsFn extends Fn { 27 | return: this["args"] extends [infer a, infer b, ...any] 28 | ? Equal 29 | : never; 30 | } 31 | 32 | export type Equals = PartialApply; 33 | 34 | export type NotEqual = Compose< 35 | [Not, PartialApply] 36 | >; 37 | 38 | export type DoesNotExtend = Compose< 39 | [Not, PartialApply] 40 | >; 41 | 42 | interface AndFn extends Fn { 43 | return: this["args"] extends [ 44 | infer first extends boolean, 45 | infer second extends boolean, 46 | ...any 47 | ] 48 | ? Every<[first, second]> 49 | : never; 50 | } 51 | 52 | export type And = PartialApply; 53 | 54 | interface OrFn extends Fn { 55 | return: this["args"] extends [ 56 | infer first extends boolean, 57 | infer second extends boolean, 58 | ...any 59 | ] 60 | ? Some<[first, second]> 61 | : never; 62 | } 63 | 64 | export type Or = PartialApply; 65 | 66 | interface XOrFn extends Fn { 67 | return: this["args"] extends [ 68 | infer first extends boolean, 69 | infer second extends boolean, 70 | ...any 71 | ] 72 | ? first extends second 73 | ? false 74 | : true 75 | : never; 76 | } 77 | 78 | export type XOr = PartialApply; 79 | } 80 | -------------------------------------------------------------------------------- /src/internals/core/Core.ts: -------------------------------------------------------------------------------- 1 | import { ExcludePlaceholders, MergeArgs } from "./impl/MergeArgs"; 2 | import { Head } from "../helpers"; 3 | 4 | declare const rawArgs: unique symbol; 5 | type rawArgs = typeof rawArgs; 6 | 7 | /** 8 | * Base interface for all functions. 9 | * 10 | * @description You need to extend this interface to create a function 11 | * that can be composed with other HOTScript functions. 12 | * Usually you will just convert some utility type you already have 13 | * by wrapping it inside a HOTScript function. 14 | * 15 | * Use `this['args']`, `this['arg0']`, `this['arg1']` etc to access 16 | * function arguments. 17 | * 18 | * The `return` property is the value returned by your function. 19 | * 20 | * @example 21 | * ```ts 22 | * export interface CustomOmitFn extends Fn { 23 | * return: Omit 24 | * } 25 | * 26 | * type T = Call // { b, c } 27 | * ``` 28 | */ 29 | export interface Fn { 30 | [rawArgs]: unknown; 31 | args: this[rawArgs] extends infer args extends unknown[] ? args : never; 32 | arg0: this[rawArgs] extends [infer arg, ...any] ? arg : never; 33 | arg1: this[rawArgs] extends [any, infer arg, ...any] ? arg : never; 34 | arg2: this[rawArgs] extends [any, any, infer arg, ...any] ? arg : never; 35 | arg3: this[rawArgs] extends [any, any, any, infer arg, ...any] ? arg : never; 36 | return: unknown; 37 | } 38 | 39 | declare const unset: unique symbol; 40 | declare const _: unique symbol; 41 | 42 | /** 43 | * A placeholder type that can be used to indicate that a parameter is not set. 44 | */ 45 | export type unset = typeof unset; 46 | 47 | /** 48 | * A placeholder type that can be used to indicate that a parameter is to partially applied. 49 | */ 50 | export type _ = typeof _; 51 | 52 | export interface arg extends Fn { 53 | return: this["args"][Index] extends infer arg extends Constraint 54 | ? arg 55 | : never; 56 | } 57 | 58 | export interface args extends Fn { 59 | return: this["args"] extends infer args extends Constraint ? args : never; 60 | } 61 | 62 | export type arg0 = arg<0, Constraint>; 63 | export type arg1 = arg<1, Constraint>; 64 | export type arg2 = arg<2, Constraint>; 65 | export type arg3 = arg<3, Constraint>; 66 | 67 | /** 68 | * Call a HOTScript function with the given arguments. 69 | * 70 | * @param fn - The function to call. 71 | * @param args - The arguments to pass to the function. 72 | * @returns The result of the function. 73 | * 74 | * @example 75 | * ```ts 76 | * type T0 = Apply; // 3 77 | * ``` 78 | */ 79 | export type Apply = (fn & { 80 | [rawArgs]: args; 81 | })["return"]; 82 | 83 | /** 84 | * Calls a HOTScript function. 85 | * 86 | * @param fn - The function to call. 87 | * @param ...args - optional arguments 88 | * 89 | * @example 90 | * ```ts 91 | * type T0 = Call>; // 3 92 | * type T1 = Call, 2>; // 3 93 | * type T2 = Call; // 3 94 | * type T3 = Call< 95 | * Tuples.Map, ["a.b", "b.c"]> 96 | * >; // [["a", "b"], ["b", "c"]] 97 | * ``` 98 | */ 99 | export type Call< 100 | fn extends Fn, 101 | arg0 = _, 102 | arg1 = _, 103 | arg2 = _, 104 | arg3 = _ 105 | > = (fn & { 106 | [rawArgs]: ExcludePlaceholders<[arg0, arg1, arg2, arg3]>; 107 | })["return"]; 108 | 109 | /** 110 | * Pipe a value through a list of functions. 111 | * @description This is the same as the pipe operator in other languages. 112 | * Calls the first function with the initial value, then passes the result to the second function, and so on. 113 | * 114 | * @param acc - The initial value to pass to the first function. 115 | * @param xs - The list of functions to pipe the value through. 116 | * @returns The result of the last function. 117 | * 118 | * @example 119 | * ```ts 120 | * type T0 = Pipe<1, [Numbers.Add<1>, Numbers.Negate]>; // -2 121 | * ``` 122 | */ 123 | export type Pipe = xs extends [ 124 | infer first extends Fn, 125 | ...infer rest extends Fn[] 126 | ] 127 | ? Pipe, rest> 128 | : acc; 129 | 130 | /** 131 | * Pipe a value through a list of functions. 132 | * @description This is the same as the pipe operator in other languages. 133 | * Calls the last function with the initial value, then passes the result to the second to last function, and so on. 134 | * 135 | * @param xs - The list of functions to pipe the value through. 136 | * @param acc - The initial value to pass to the last function. 137 | * @returns The result of the first function. 138 | * 139 | * @example 140 | * ```ts 141 | * type T0 = PipeRight<[Numbers.Add<1>, Numbers.Negate], 1>; // 0 142 | */ 143 | export type PipeRight = xs extends [ 144 | ...infer rest extends Fn[], 145 | infer last extends Fn 146 | ] 147 | ? PipeRight> 148 | : acc; 149 | 150 | /** 151 | * Returns the the function's first argument. 152 | * 153 | * @param arg0 - The function to extract the first argument from. 154 | * @returns The first argument of the function. 155 | */ 156 | export interface Identity extends Fn { 157 | return: this["arg0"]; 158 | } 159 | 160 | /** 161 | * A function that returns it's generic parameter. 162 | * 163 | * @param T - The type to return. 164 | * @returns The type `T`. 165 | */ 166 | export interface Constant extends Fn { 167 | return: T; 168 | } 169 | 170 | /** 171 | * Composes a list of functions into a single function that passes the result of each function to the next. 172 | * Executes the functions from right to left. 173 | * 174 | * @param fns - The list of functions to compose. 175 | * @returns The composed function. 176 | * 177 | * @example 178 | * ```ts 179 | * type T0 = Call,S.Split<'.'> ]>, 'a.b.c'>; // 'a-b-c' 180 | * ``` 181 | */ 182 | export interface Compose extends Fn { 183 | return: ComposeImpl; 184 | } 185 | 186 | type ComposeImpl = fns extends [ 187 | ...infer rest extends Fn[], 188 | infer last extends Fn 189 | ] 190 | ? ComposeImpl]> 191 | : Head; 192 | 193 | /** 194 | * Composes a list of functions into a single function that passes the result of each function to the next. 195 | * Executes the functions from left to right. 196 | * 197 | * @param fns - The list of functions to compose. 198 | * @returns The composed function. 199 | * 200 | * @example 201 | * ```ts 202 | * type T0 = Call,T.Join<'-'> ]>, 'a.b.c'>; // 'a-b-c' 203 | * ``` 204 | */ 205 | export interface ComposeLeft extends Fn { 206 | return: ComposeLeftImpl; 207 | } 208 | 209 | type ComposeLeftImpl = fns extends [ 210 | infer first extends Fn, 211 | ...infer rest extends Fn[] 212 | ] 213 | ? ComposeLeftImpl]> 214 | : Head; 215 | 216 | /** 217 | * `PartialApply` Pre applies some arguments to a function. 218 | * it takes a `Fn`, and a list of pre applied arguments, 219 | * and returns a new function taking the rest of these arguments. 220 | * 221 | * Most functions in HOTScript are already partially applicable (curried). 222 | * 223 | * @param fn - The function to partially apply. 224 | * @param partialArgs - The arguments to partially apply. 225 | * @returns The partially applied function. 226 | * 227 | * @example 228 | * ```ts 229 | * interface Append extends Fn { 230 | * return: [...this['arg1'], this['arg0']] 231 | * } 232 | * 233 | * type Append1 = PartialApply 234 | * type T0 = Call; // [0, 1] 235 | * 236 | * type AppendTo123 = PartialApply 237 | * type T1 = Call; // [1, 2, 3, 4] 238 | */ 239 | export interface PartialApply 240 | extends Fn { 241 | return: MergeArgs< 242 | this["args"], 243 | partialArgs 244 | > extends infer args extends unknown[] 245 | ? Apply 246 | : never; 247 | } 248 | -------------------------------------------------------------------------------- /src/internals/core/impl/MergeArgs.ts: -------------------------------------------------------------------------------- 1 | import { unset, _ } from "../../core/Core"; 2 | import { Equal, IsNever } from "../../helpers"; 3 | 4 | export type ExcludePlaceholders = xs extends [ 5 | infer first, 6 | ...infer rest 7 | ] 8 | ? Equal extends true 9 | ? ExcludePlaceholders 10 | : ExcludePlaceholders 11 | : output; 12 | 13 | type MergeArgsRec< 14 | pipedArgs extends any[], 15 | partialArgs extends any[], 16 | output extends any[] = [] 17 | > = partialArgs extends [infer partialFirst, ...infer partialRest] 18 | ? IsNever extends true 19 | ? MergeArgsRec 20 | : [partialFirst] extends [_] 21 | ? pipedArgs extends [infer pipedFirst, ...infer pipedRest] 22 | ? MergeArgsRec 23 | : [...output, ...ExcludePlaceholders] 24 | : MergeArgsRec 25 | : [...output, ...pipedArgs]; 26 | 27 | type EmptyIntoPlaceholder = IsNever extends true 28 | ? never 29 | : [x] extends [unset] 30 | ? _ 31 | : x; 32 | 33 | type MapEmptyIntoPlaceholder = xs extends [ 34 | infer first, 35 | ...infer rest 36 | ] 37 | ? MapEmptyIntoPlaceholder]> 38 | : output; 39 | 40 | export type MergeArgs< 41 | pipedArgs extends any[], 42 | partialArgs extends any[] 43 | > = MergeArgsRec>; 44 | -------------------------------------------------------------------------------- /src/internals/functions/Functions.ts: -------------------------------------------------------------------------------- 1 | import { Fn, PartialApply, unset, _, Call } from "../core/Core"; 2 | 3 | export namespace Functions { 4 | type ParametersImpl = fn extends (...args: infer args) => any 5 | ? args 6 | : never; 7 | 8 | export interface ParametersFn extends Fn { 9 | return: ParametersImpl; 10 | } 11 | 12 | /** 13 | * Returns the parameters of a function. 14 | * 15 | * @param fn - The function to extract the parameters from. 16 | * @returns The parameters of the function. 17 | * 18 | * @example 19 | * ```ts 20 | * type T0 = Call void>; // [a: number,b: string] 21 | * ``` 22 | */ 23 | export type Parameters< 24 | fn extends ((...args: any[]) => any) | _ | unset = unset 25 | > = PartialApply; 26 | 27 | export interface ParameterFn extends Fn { 28 | return: ParametersImpl[this["arg1"]]; 29 | } 30 | /** 31 | * Returns the Nth parameter of a function. 32 | * 33 | * @param fn - The function to extract the parameter from. 34 | * @param N - The index of the parameter to return. 35 | * @returns The Nth parameter of the function. 36 | * 37 | * @example 38 | * ```ts 39 | * type T0 = Call, (a: number, b: string) => void>; // number 40 | * type T1 = Call void>; // string 41 | * type T2 = Call void>>; // number 42 | */ 43 | export type Parameter< 44 | N extends number | _ | unset = unset, 45 | fn extends ((...args: any[]) => any) | _ | unset = unset 46 | > = PartialApply; 47 | 48 | type ReturnTypeImpl = fn extends (...args: any[]) => infer ret 49 | ? ret 50 | : never; 51 | 52 | /** 53 | * ReturnTypes the return type of a function. 54 | * 55 | * @param fn - The function to extract the return type from. 56 | * @returns The return type of the function. 57 | * 58 | * @example 59 | * ```ts 60 | * type T0 = Call number>; // number 61 | * type T1 = Call number>>; // number 62 | * ``` 63 | */ 64 | export type ReturnType< 65 | fn extends ((...args: any[]) => any) | _ | unset = unset 66 | > = PartialApply; 67 | export interface ReturnTypeFn extends Fn { 68 | return: ReturnTypeImpl; 69 | } 70 | 71 | /** 72 | * Transforms the return type of a function type. 73 | * 74 | * @param fn - Type-level function to call on the return type. 75 | * @param fnValue - The function type to update. 76 | * @returns a function type with an updated return type. 77 | * 78 | * @example 79 | * ```ts 80 | * type T0 = Call< 81 | * Functions.MapReturnType, 82 | * (a: number, b: string) => 1 | 2 83 | * >; 84 | * // => (a: number, b: string) => "1" | "2" 85 | * ``` 86 | */ 87 | export type MapReturnType< 88 | fn extends Fn | unset | _ = unset, 89 | fnValue extends ((...args: any[]) => any) | _ | unset = unset 90 | > = PartialApply; 91 | 92 | export interface MapReturnTypeFn extends Fn { 93 | return: this["args"] extends [infer fn extends Fn, infer fnValue] 94 | ? fnValue extends (...args: infer args) => infer returnType 95 | ? (...args: args) => Call 96 | : never 97 | : never; 98 | } 99 | 100 | /** 101 | * Transforms the paramaters of a function type. 102 | * 103 | * @param fn - Type-level function to call on parameters. 104 | * @param fnValue - The function type to update. 105 | * @returns a function type with updated parameters. 106 | * 107 | * @example 108 | * ```ts 109 | * type T0 = Call< 110 | F.MapParameters>, 111 | (a: "1" | "2", b: "3" | "4") => void 112 | >; 113 | * // => (a: 1 | 2, b: 3 | 4) => void 114 | * ``` 115 | */ 116 | export type MapParameters< 117 | fn extends Fn | unset | _ = unset, 118 | fnValue extends ((...args: any[]) => any) | _ | unset = unset 119 | > = PartialApply; 120 | 121 | export interface MapParametersFn extends Fn { 122 | return: this["args"] extends [infer fn extends Fn, infer fnValue] 123 | ? fnValue extends (...args: infer args) => infer returnType 124 | ? (...args: Extract, readonly any[]>) => returnType 125 | : never 126 | : never; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/internals/helpers.ts: -------------------------------------------------------------------------------- 1 | export type Equal = (() => T extends a ? 1 : 2) extends < 2 | T 3 | >() => T extends b ? 1 : 2 4 | ? true 5 | : false; 6 | 7 | /** 8 | * HACK: 9 | * Special function for never because `Equal` doesn't 10 | * work when called deep in the call stack (for a reason I don't understand 11 | * probably a TS bug). 12 | */ 13 | export type IsNever = [T] extends [never] ? true : false; 14 | 15 | export type Expect = a; 16 | 17 | export type Some = true extends bools[number] 18 | ? true 19 | : false; 20 | 21 | export type Every = bools[number] extends true 22 | ? true 23 | : false; 24 | 25 | export type Extends = [a] extends [b] ? true : false; 26 | 27 | export type Not = a extends true ? false : true; 28 | 29 | /** 30 | * trick to combine multiple unions of objects into a single object 31 | * only works with objects not primitives 32 | * @param union - Union of objects 33 | * @returns Intersection of objects 34 | */ 35 | export type UnionToIntersection = ( 36 | union extends any ? (k: union) => void : never 37 | ) extends (k: infer intersection) => void 38 | ? intersection 39 | : never; 40 | 41 | export type Prettify = { [K in keyof T]: T[K] } | never; 42 | 43 | export type RecursivePrettify = IsArrayStrict extends true 44 | ? RecursivePrettify[number]>[] 45 | : { [K in keyof T]: RecursivePrettify }; 46 | 47 | export type AnyTuple = readonly [any, ...any]; 48 | 49 | export namespace Iterator { 50 | export type Get = it["length"]; 51 | 52 | export type Iterator< 53 | n extends number, 54 | it extends any[] = [] 55 | > = it["length"] extends n ? it : Iterator; 56 | 57 | export type Next = [any, ...it]; 58 | export type Prev = it extends readonly [any, ...infer tail] 59 | ? tail 60 | : []; 61 | } 62 | 63 | type UppercaseLetter = 64 | | "A" 65 | | "B" 66 | | "C" 67 | | "D" 68 | | "E" 69 | | "F" 70 | | "G" 71 | | "H" 72 | | "I" 73 | | "J" 74 | | "K" 75 | | "L" 76 | | "M" 77 | | "N" 78 | | "O" 79 | | "P" 80 | | "Q" 81 | | "R" 82 | | "S" 83 | | "T" 84 | | "U" 85 | | "V" 86 | | "W" 87 | | "X" 88 | | "Y" 89 | | "Z"; 90 | 91 | type KebabToCamel = str extends `${infer first}-${infer rest}` 92 | ? `${first}${KebabToCamel>}` 93 | : str; 94 | 95 | type SnakeToCamel = str extends `${infer first}_${infer rest}` 96 | ? `${first}${SnakeToCamel>}` 97 | : str; 98 | 99 | /** 100 | * Converts string casing from snake_case or kebab-case to camelCase. 101 | */ 102 | export type CamelCase = KebabToCamel>; 103 | 104 | /** 105 | * Converts string casing from camelCase or kebab-case to snake_case. 106 | */ 107 | export type SnakeCase< 108 | str, 109 | output extends string = "" 110 | > = str extends `${infer first}${infer rest}` 111 | ? first extends UppercaseLetter 112 | ? output extends "" 113 | ? SnakeCase> 114 | : SnakeCase}`> 115 | : first extends "-" 116 | ? SnakeCase 117 | : SnakeCase 118 | : output extends "" 119 | ? str 120 | : output; 121 | 122 | /** 123 | * Converts string casing from camelCase or snake_case to kebab-case. 124 | */ 125 | export type KebabCase< 126 | str, 127 | output extends string = "" 128 | > = str extends `${infer first}${infer rest}` 129 | ? first extends UppercaseLetter 130 | ? output extends "" 131 | ? KebabCase> 132 | : KebabCase}`> 133 | : first extends "_" 134 | ? KebabCase 135 | : KebabCase 136 | : output extends "" 137 | ? str 138 | : output; 139 | 140 | export type IsTuple = a extends 141 | | readonly [] 142 | | readonly [any, ...any] 143 | | readonly [...any, any] 144 | ? true 145 | : false; 146 | 147 | export type IsArrayStrict = a extends readonly any[] 148 | ? Not> 149 | : false; 150 | 151 | /** 152 | * get last element of union 153 | * @param Union - Union of any types 154 | * @returns Last element of union 155 | */ 156 | type GetUnionLast = UnionToIntersection< 157 | Union extends any ? () => Union : never 158 | > extends () => infer Last 159 | ? Last 160 | : never; 161 | 162 | /** 163 | * Convert union to tuple 164 | * @param Union - Union of any types, can be union of complex, composed or primitive types 165 | * @returns Tuple of each elements in the union 166 | */ 167 | export type UnionToTuple = [ 168 | Union 169 | ] extends [never] 170 | ? Tuple 171 | : UnionToTuple< 172 | Exclude>, 173 | [GetUnionLast, ...Tuple] 174 | >; 175 | 176 | /** 177 | * Split string into a tuple, using a simple string literal separator 178 | * @description - This is a simple implementation of split, it does not support multiple separators 179 | * A more complete implementation is built on top of this one 180 | * @param Str - String to split 181 | * @param Sep - Separator, must be a string literal not a union of string literals 182 | * @returns Tuple of strings 183 | */ 184 | export type Split< 185 | Str, 186 | Sep extends string, 187 | Acc extends string[] = [] 188 | > = Str extends "" 189 | ? Acc 190 | : Str extends `${infer T}${Sep}${infer U}` 191 | ? Split 192 | : [...Acc, Str]; 193 | 194 | export type Stringifiable = 195 | | string 196 | | number 197 | | boolean 198 | | bigint 199 | | null 200 | | undefined; 201 | 202 | export type Primitive = 203 | | string 204 | | number 205 | | boolean 206 | | bigint 207 | | null 208 | | undefined 209 | | symbol; 210 | 211 | export type Head = xs extends [infer first, ...any] ? first : never; 212 | -------------------------------------------------------------------------------- /src/internals/match/Match.ts: -------------------------------------------------------------------------------- 1 | import { Fn, PartialApply, unset, _ } from "../core/Core"; 2 | import * as Impl from "./impl/match"; 3 | 4 | /** 5 | * Match creates a pattern matching expression. 6 | * Pattern matching let you execute different 7 | * branches of code based on the structure 8 | * of the input type exdestructure the input type, and execute 9 | * 10 | * @param input - the input to pattern match on 11 | * @param withClauses - list of with clauses representing different branches if code 12 | * 13 | * @example 14 | * ```ts 15 | * type Test = Match>, 17 | * With>, 18 | * With> 19 | * ]> 20 | * ``` 21 | */ 22 | export type Match< 23 | valueOrWithClauses = unset, 24 | withClauses extends Impl.With[] | unset | _ = unset 25 | > = PartialApply< 26 | MatchFn, 27 | withClauses extends unset 28 | ? [unset, valueOrWithClauses] 29 | : [valueOrWithClauses, withClauses] 30 | >; 31 | 32 | interface MatchFn extends Fn { 33 | return: Impl.Match; 34 | } 35 | 36 | export namespace Match { 37 | export type With = Impl.With; 38 | } 39 | -------------------------------------------------------------------------------- /src/internals/match/impl/match.ts: -------------------------------------------------------------------------------- 1 | import { arg, Call, Fn, PartialApply, unset } from "../../core/Core"; 2 | import { Primitive, UnionToIntersection } from "../../helpers"; 3 | 4 | type GetWithDefault = K extends keyof Obj ? Obj[K] : Def; 5 | 6 | type ReplaceArgsWithConstraint = pattern extends arg< 7 | any, 8 | infer Constraint 9 | > 10 | ? Constraint 11 | : pattern extends Primitive 12 | ? pattern 13 | : pattern extends [any, ...any] 14 | ? { [key in keyof pattern]: ReplaceArgsWithConstraint } 15 | : pattern extends (infer V)[] 16 | ? ReplaceArgsWithConstraint[] 17 | : pattern extends object 18 | ? { [key in keyof pattern]: ReplaceArgsWithConstraint } 19 | : pattern; 20 | 21 | type DoesMatch = 22 | value extends ReplaceArgsWithConstraint ? true : false; 23 | 24 | type ExtractArgObject = pattern extends arg 25 | ? { [K in N]: value } 26 | : pattern extends [] 27 | ? {} 28 | : [value, pattern] extends [ 29 | [infer valueFirst, ...infer valueRest], 30 | [infer patternFirst, ...infer patternRest] 31 | ] 32 | ? ExtractArgObject & 33 | ExtractArgObject 34 | : [value, pattern] extends [(infer valueFirst)[], (infer patternFirst)[]] 35 | ? ExtractArgObject 36 | : [value, pattern] extends [object, object] 37 | ? UnionToIntersection< 38 | { 39 | [k in keyof value & keyof pattern]: ExtractArgObject< 40 | value[k], 41 | pattern[k] 42 | >; 43 | }[keyof value & keyof pattern] 44 | > 45 | : {}; 46 | 47 | type WithDefaultArgs = [Args[number]] extends [unset] 48 | ? Def 49 | : Args; 50 | 51 | type ArgObjectToArgs = [ 52 | GetWithDefault, 53 | GetWithDefault, 54 | GetWithDefault, 55 | GetWithDefault 56 | ]; 57 | 58 | type ExtractArgs = WithDefaultArgs< 59 | ArgObjectToArgs>, 60 | [value] 61 | >; 62 | 63 | export type Match[]> = patterns extends [ 64 | With, 65 | ...infer restPatterns extends With[] 66 | ] 67 | ? DoesMatch extends true 68 | ? handler extends Fn 69 | ? Call, ExtractArgs>> 70 | : handler 71 | : Match 72 | : never; 73 | 74 | export type With = { pattern: pattern; handler: handler }; 75 | -------------------------------------------------------------------------------- /src/internals/numbers/Numbers.ts: -------------------------------------------------------------------------------- 1 | import { Fn, PartialApply, unset, _ } from "../core/Core"; 2 | import * as Impl from "./impl/numbers"; 3 | 4 | export namespace Numbers { 5 | /** 6 | * Add two numbers together 7 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 8 | * @param n1 - the first number 9 | * @param n2 - the second number 10 | * @returns the sum of the two numbers 11 | * @example 12 | * ```ts 13 | * type T0 = Call>; // 3 14 | * type T1 = Call>; // 1000000000000000000000000001n 15 | * ``` 16 | */ 17 | export type Add< 18 | n1 extends number | bigint | _ | unset = unset, 19 | n2 extends number | bigint | _ | unset = unset 20 | > = PartialApply; 21 | 22 | interface AddFn extends Fn { 23 | return: this["args"] extends [ 24 | infer a extends number | bigint, 25 | infer b extends number | bigint, 26 | ...any 27 | ] 28 | ? Impl.Add 29 | : never; 30 | } 31 | 32 | /** 33 | * Subtract two numbers 34 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 35 | * @param n1 - the first number 36 | * @param n2 - the second number to subtract from the first 37 | * @returns the difference of the two numbers 38 | * @example 39 | * ```ts 40 | * type T0 = Call>; // -1 41 | * type T1 = Call>; // 999999999999999999999999999n 42 | * ``` 43 | */ 44 | export type Sub< 45 | n1 extends number | bigint | _ | unset = unset, 46 | n2 extends number | bigint | _ | unset = unset 47 | > = PartialApply; 48 | 49 | interface SubFn extends Fn { 50 | return: this["args"] extends [ 51 | infer a extends number | bigint, 52 | infer b extends number | bigint, 53 | ...any 54 | ] 55 | ? Impl.Sub 56 | : never; 57 | } 58 | 59 | /** 60 | * Multiply two numbers together 61 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 62 | * @param n1 - the first number 63 | * @param n2 - the second number 64 | * @returns the product of the two numbers 65 | * @example 66 | * ```ts 67 | * type T0 = Call>; // 297 68 | * type T1 = Call>; // 1999999999999999999999999998n 69 | * ``` 70 | */ 71 | export type Mul< 72 | n1 extends number | bigint | _ | unset = unset, 73 | n2 extends number | bigint | _ | unset = unset 74 | > = PartialApply; 75 | 76 | interface MulFn extends Fn { 77 | return: this["args"] extends [ 78 | infer a extends number | bigint, 79 | infer b extends number | bigint, 80 | ...any 81 | ] 82 | ? Impl.Mul 83 | : never; 84 | } 85 | 86 | /** 87 | * Divide two numbers 88 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 89 | * @param n1 - the first number 90 | * @param n2 - the second number to divide the first by 91 | * @returns the quotient of the two numbers 92 | * @example 93 | * ```ts 94 | * type T0 = Call>; // 33 95 | * type T1 = Call>; // 249999999999999999999999999n 96 | * ``` 97 | */ 98 | export type Div< 99 | n1 extends number | bigint | _ | unset = unset, 100 | n2 extends number | bigint | _ | unset = unset 101 | > = PartialApply; 102 | 103 | interface DivFn extends Fn { 104 | return: this["args"] extends [ 105 | infer a extends number | bigint, 106 | infer b extends number | bigint, 107 | ...any 108 | ] 109 | ? Impl.Div 110 | : never; 111 | } 112 | 113 | /** 114 | * Modulo of two numbers 115 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 116 | * @param n1 - the first number 117 | * @param n2 - the second number to divide the first by 118 | * @returns the remainder of the two numbers 119 | * @example 120 | * ```ts 121 | * type T0 = Call>; // 1 122 | * type T1 = Call>; // 3n 123 | * ``` 124 | */ 125 | export type Mod< 126 | n1 extends number | bigint | _ | unset = unset, 127 | n2 extends number | bigint | _ | unset = unset 128 | > = PartialApply; 129 | 130 | interface ModFn extends Fn { 131 | return: this["args"] extends [ 132 | infer a extends number | bigint, 133 | infer b extends number | bigint, 134 | ...any 135 | ] 136 | ? Impl.Mod 137 | : never; 138 | } 139 | 140 | /** 141 | * Negate a number 142 | * @description the number can be of different types (bigint or number) and handle really large numbers 143 | * @param n - the number to negate 144 | * @returns the negated number 145 | * @example 146 | * ```ts 147 | * type T0 = Call>; // -1 148 | * type T1 = Call>; // -999999999999999999999999999n 149 | * ``` 150 | */ 151 | export type Negate = 152 | PartialApply; 153 | 154 | interface NegateFn extends Fn { 155 | return: this["args"] extends [infer a extends number | bigint, ...any] 156 | ? Impl.Negate 157 | : never; 158 | } 159 | 160 | /** 161 | * Absolute value of a number 162 | * @description the number can be of different types (bigint or number) and handle really large numbers 163 | * @param n - the number to get the absolute value of 164 | * @returns the absolute value of the number 165 | * @example 166 | * ```ts 167 | * type T0 = Call>; // 1 168 | * type T1 = Call>; // 999999999999999999999999999n 169 | * ``` 170 | */ 171 | export type Abs = PartialApply< 172 | AbsFn, 173 | [n] 174 | >; 175 | 176 | export interface AbsFn extends Fn { 177 | return: this["args"] extends [infer a extends number | bigint, ...any] 178 | ? Impl.Abs 179 | : never; 180 | } 181 | 182 | /** 183 | * Returns the max between 2 numbers. 184 | * @param n1 - first number or bigint 185 | * @param n2 - second number or bigint 186 | * @returns the maximum values between the two 187 | * @example 188 | * ```ts 189 | * type T0 = Call>; // 2 190 | * ``` 191 | */ 192 | export type Max< 193 | n1 extends number | bigint | _ | unset = unset, 194 | n2 extends number | bigint | _ | unset = unset 195 | > = PartialApply; 196 | 197 | export interface MaxFn extends Fn { 198 | return: Impl.Max< 199 | Extract, 200 | Extract 201 | >; 202 | } 203 | 204 | /** 205 | * Returns the min between 2 numbers. 206 | * @param n1 - first number or bigint 207 | * @param n2 - second number or bigint 208 | * @returns the minimum values between the two 209 | * @example 210 | * ```ts 211 | * type T0 = Call>; // 1 212 | * ``` 213 | */ 214 | export type Min< 215 | n1 extends number | bigint | _ | unset = unset, 216 | n2 extends number | bigint | _ | unset = unset 217 | > = PartialApply; 218 | 219 | export interface MinFn extends Fn { 220 | return: Impl.Min< 221 | Extract, 222 | Extract 223 | >; 224 | } 225 | 226 | /** 227 | * Power of a number 228 | * @description the number can be of different types (bigint or number) and handle really large numbers 229 | * @param n1 - the base number 230 | * @param n2 - the exponent 231 | * @returns the power of the two numbers 232 | * @example 233 | * ```ts 234 | * type T0 = Call>; // 340282366920938463463374607431768211456 235 | * ``` 236 | */ 237 | export type Power< 238 | n1 extends number | bigint | _ | unset = unset, 239 | n2 extends number | bigint | _ | unset = unset 240 | > = PartialApply; 241 | 242 | interface PowerFn extends Fn { 243 | return: this["args"] extends [ 244 | infer a extends number | bigint, 245 | infer b extends number | bigint, 246 | ...any 247 | ] 248 | ? Impl.Power 249 | : never; 250 | } 251 | 252 | /** 253 | * Compare two numbers 254 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 255 | * @param n1 - the first number 256 | * @param n2 - the second number to compare the first to 257 | * @returns -1 if n1 < n2, 0 if n1 === n2, 1 if n1 > n2 258 | * @example 259 | * ```ts 260 | * type T0 = Call>; // -1 261 | * type T1 = Call>; // 1 262 | * type T2 = Call>; // 0 263 | * ``` 264 | */ 265 | export type Compare< 266 | n1 extends number | bigint | _ | unset = unset, 267 | n2 extends number | bigint | _ | unset = unset 268 | > = PartialApply; 269 | 270 | interface CompareFn extends Fn { 271 | return: this["args"] extends [ 272 | infer a extends number | bigint, 273 | infer b extends number | bigint, 274 | ...any 275 | ] 276 | ? Impl.Compare 277 | : never; 278 | } 279 | 280 | /** 281 | * Check if two numbers are equal 282 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 283 | * @param n1 - the first number 284 | * @param n2 - the second number to compare the first to 285 | * @returns true if n1 === n2, false otherwise 286 | * @example 287 | * ```ts 288 | * type T0 = Call>; // false 289 | * type T1 = Call>; // true 290 | * ``` 291 | */ 292 | export type Equal< 293 | n1 extends number | bigint | _ | unset = unset, 294 | n2 extends number | bigint | _ | unset = unset 295 | > = PartialApply; 296 | 297 | interface EqualFn extends Fn { 298 | return: this["args"] extends [ 299 | infer a extends number | bigint, 300 | infer b extends number | bigint, 301 | ...any 302 | ] 303 | ? Impl.Equal 304 | : never; 305 | } 306 | 307 | /** 308 | * Check if two numbers are not equal 309 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 310 | * @param n1 - the first number 311 | * @param n2 - the second number to compare the first to 312 | * @returns true if n1 !== n2, false otherwise 313 | * @example 314 | * ```ts 315 | * type T0 = Call>; // true 316 | * type T1 = Call>; // false 317 | * ``` 318 | */ 319 | export type NotEqual< 320 | n1 extends number | bigint | _ | unset = unset, 321 | n2 extends number | bigint | _ | unset = unset 322 | > = PartialApply; 323 | 324 | interface NotEqualFn extends Fn { 325 | return: this["args"] extends [ 326 | infer a extends number | bigint, 327 | infer b extends number | bigint, 328 | ...any 329 | ] 330 | ? Impl.NotEqual 331 | : never; 332 | } 333 | 334 | /** 335 | * Check if a number is less than another 336 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 337 | * @param n1 - the first number 338 | * @param n2 - the second number to compare the first to 339 | * @returns true if n1 < n2, false otherwise 340 | * @example 341 | * ```ts 342 | * type T0 = Call>; // true 343 | * type T1 = Call>; // false 344 | * type T2 = Call>; // false 345 | * ``` 346 | */ 347 | export type LessThan< 348 | n1 extends number | bigint | _ | unset = unset, 349 | n2 extends number | bigint | _ | unset = unset 350 | > = PartialApply; 351 | 352 | interface LessThanFn extends Fn { 353 | return: this["args"] extends [ 354 | infer a extends number | bigint, 355 | infer b extends number | bigint, 356 | ...any 357 | ] 358 | ? Impl.LessThan 359 | : never; 360 | } 361 | 362 | /** 363 | * Check if a number is less than or equal to another 364 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 365 | * @param n1 - the first number 366 | * @param n2 - the second number to compare the first to 367 | * @returns true if n1 <= n2, false otherwise 368 | * @example 369 | * ```ts 370 | * type T0 = Call>; // true 371 | * type T1 = Call>; // true 372 | * type T2 = Call>; // false 373 | * ``` 374 | */ 375 | export type LessThanOrEqual< 376 | n1 extends number | bigint | _ | unset = unset, 377 | n2 extends number | bigint | _ | unset = unset 378 | > = PartialApply< 379 | LessThanOrEqualFn, 380 | n2 extends unset ? [unset, n1] : [n1, n2] 381 | >; 382 | 383 | interface LessThanOrEqualFn extends Fn { 384 | return: this["args"] extends [ 385 | infer a extends number | bigint, 386 | infer b extends number | bigint, 387 | ...any 388 | ] 389 | ? Impl.LessThanOrEqual 390 | : never; 391 | } 392 | 393 | /** 394 | * Check if a number is greater than another 395 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 396 | * @param n1 - the first number 397 | * @param n2 - the second number to compare the first to 398 | * @returns true if n1 > n2, false otherwise 399 | * @example 400 | * ```ts 401 | * type T0 = Call>; // false 402 | * type T1 = Call>; // false 403 | * type T2 = Call>; // true 404 | * ``` 405 | */ 406 | export type GreaterThan< 407 | n1 extends number | bigint | _ | unset = unset, 408 | n2 extends number | bigint | _ | unset = unset 409 | > = PartialApply; 410 | 411 | interface GreaterThanFn extends Fn { 412 | return: this["args"] extends [ 413 | infer a extends number | bigint, 414 | infer b extends number | bigint, 415 | ...any 416 | ] 417 | ? Impl.GreaterThan 418 | : never; 419 | } 420 | 421 | /** 422 | * Check if a number is greater than or equal to another 423 | * @description the two numbers can be of different types (bigint or number) and handle really large numbers 424 | * @param n1 - the first number 425 | * @param n2 - the second number to compare the first to 426 | * @returns true if n1 >= n2, false otherwise 427 | * @example 428 | * ```ts 429 | * type T0 = Call>; // false 430 | * type T1 = Call>; // true 431 | * type T2 = Call>; // true 432 | * ``` 433 | */ 434 | export type GreaterThanOrEqual< 435 | n1 extends number | bigint | _ | unset = unset, 436 | n2 extends number | bigint | _ | unset = unset 437 | > = PartialApply< 438 | GreaterThanOrEqualFn, 439 | n2 extends unset ? [unset, n1] : [n1, n2] 440 | >; 441 | 442 | interface GreaterThanOrEqualFn extends Fn { 443 | return: this["args"] extends [ 444 | infer a extends number | bigint, 445 | infer b extends number | bigint, 446 | ...any 447 | ] 448 | ? Impl.GreaterThanOrEqual 449 | : never; 450 | } 451 | } 452 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/absolute.ts: -------------------------------------------------------------------------------- 1 | export type Abs = `${T}` extends `-${infer U extends 2 | | number 3 | | bigint}` 4 | ? U 5 | : T; 6 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/addition.ts: -------------------------------------------------------------------------------- 1 | import { AddDigits } from "./digits/addition"; 2 | import type { 3 | ToNumber, 4 | ToString, 5 | Digit, 6 | DigitNumber, 7 | FromDigitNumber, 8 | ToDigitNumber, 9 | Sign, 10 | Num, 11 | MakeDigitNumber, 12 | InvertSign, 13 | Normalize, 14 | } from "./utils"; 15 | import { CompareDigits } from "./compare"; 16 | import { SubDigits } from "./digits/substraction"; 17 | 18 | type AddDigitNumbers< 19 | T extends DigitNumber, 20 | U extends DigitNumber 21 | > = Sign extends Sign 22 | ? MakeDigitNumber, AddDigits, Num>> 23 | : CompareDigits, Num> extends 1 24 | ? MakeDigitNumber, SubDigits, Num>> 25 | : MakeDigitNumber, SubDigits, Num>>; 26 | 27 | export type Add< 28 | T extends number | bigint, 29 | U extends number | bigint 30 | > = ToNumber< 31 | FromDigitNumber< 32 | Normalize< 33 | AddDigitNumbers>, ToDigitNumber>> 34 | > 35 | > 36 | >; 37 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/compare.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ToString, 3 | Digit, 4 | DigitNumber, 5 | ToDigitNumber, 6 | Sign, 7 | Num, 8 | } from "./utils"; 9 | import { Equal as _Equal } from "../../helpers"; 10 | 11 | export type CompareLength< 12 | T extends any[], 13 | U extends any[] 14 | > = T["length"] extends U["length"] ? 1 : 0; 15 | 16 | export type DigitCompareTable = [ 17 | [0, -1, -1, -1, -1, -1, -1, -1, -1, -1], 18 | [1, 0, -1, -1, -1, -1, -1, -1, -1, -1], 19 | [1, 1, 0, -1, -1, -1, -1, -1, -1, -1], 20 | [1, 1, 1, 0, -1, -1, -1, -1, -1, -1], 21 | [1, 1, 1, 1, 0, -1, -1, -1, -1, -1], 22 | [1, 1, 1, 1, 1, 0, -1, -1, -1, -1], 23 | [1, 1, 1, 1, 1, 1, 0, -1, -1, -1], 24 | [1, 1, 1, 1, 1, 1, 1, 0, -1, -1], 25 | [1, 1, 1, 1, 1, 1, 1, 1, 0, -1], 26 | [1, 1, 1, 1, 1, 1, 1, 1, 1, 0] 27 | ]; 28 | 29 | export type DigitCompare< 30 | D1 extends Digit, 31 | D2 extends Digit 32 | > = DigitCompareTable[D1][D2]; 33 | 34 | export type CompareDigitsWithEqualLength< 35 | T extends Digit[], 36 | U extends Digit[] 37 | > = [T, U] extends [ 38 | [infer N1 extends Digit, ...infer R1 extends Digit[]], 39 | [infer N2 extends Digit, ...infer R2 extends Digit[]] 40 | ] 41 | ? DigitCompare extends 0 42 | ? CompareDigitsWithEqualLength 43 | : DigitCompare 44 | : 0; 45 | 46 | export type CompareDigits = CompareLength< 47 | T, 48 | U 49 | > extends 1 50 | ? CompareDigitsWithEqualLength 51 | : keyof U extends keyof T 52 | ? 1 53 | : -1; 54 | 55 | export type CompareDigitNumbers< 56 | T extends DigitNumber, 57 | U extends DigitNumber 58 | > = Sign extends Sign 59 | ? Sign extends "" 60 | ? CompareDigits, Num> 61 | : CompareDigits, Num> 62 | : Sign extends "-" 63 | ? -1 64 | : 1; 65 | 66 | /** 67 | * Compare two numbers 68 | * @param T - First number 69 | * @param U - Second number 70 | * @returns 0 if T = U, 1 if T > U, -1 if T < U 71 | */ 72 | export type Compare< 73 | T extends number | bigint, 74 | U extends number | bigint 75 | > = _Equal extends true 76 | ? 0 77 | : CompareDigitNumbers>, ToDigitNumber>>; 78 | 79 | export type LessThan< 80 | T extends number | bigint, 81 | U extends number | bigint 82 | > = Compare extends -1 ? true : false; 83 | 84 | export type GreaterThan< 85 | T extends number | bigint, 86 | U extends number | bigint 87 | > = Compare extends 1 ? true : false; 88 | 89 | export type Equal< 90 | T extends number | bigint, 91 | U extends number | bigint 92 | > = _Equal; 93 | 94 | export type NotEqual< 95 | T extends number | bigint, 96 | U extends number | bigint 97 | > = _Equal extends true ? false : true; 98 | 99 | export type LessThanOrEqual< 100 | T extends number | bigint, 101 | U extends number | bigint 102 | > = Compare extends -1 | 0 ? true : false; 103 | 104 | export type GreaterThanOrEqual< 105 | T extends number | bigint, 106 | U extends number | bigint 107 | > = Compare extends 1 | 0 ? true : false; 108 | 109 | export type Max = Compare< 110 | T, 111 | U 112 | > extends 1 | 0 113 | ? T 114 | : U; 115 | 116 | export type Min = Compare< 117 | T, 118 | U 119 | > extends 1 | 0 120 | ? U 121 | : T; 122 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/digits/addition.ts: -------------------------------------------------------------------------------- 1 | import type { Digit } from "../utils"; 2 | 3 | type AddDigitTable = [ 4 | [ 5 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 6 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 0], 7 | [2, 3, 4, 5, 6, 7, 8, 9, 0, 1], 8 | [3, 4, 5, 6, 7, 8, 9, 0, 1, 2], 9 | [4, 5, 6, 7, 8, 9, 0, 1, 2, 3], 10 | [5, 6, 7, 8, 9, 0, 1, 2, 3, 4], 11 | [6, 7, 8, 9, 0, 1, 2, 3, 4, 5], 12 | [7, 8, 9, 0, 1, 2, 3, 4, 5, 6], 13 | [8, 9, 0, 1, 2, 3, 4, 5, 6, 7], 14 | [9, 0, 1, 2, 3, 4, 5, 6, 7, 8] 15 | ], 16 | [ 17 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 0], 18 | [2, 3, 4, 5, 6, 7, 8, 9, 0, 1], 19 | [3, 4, 5, 6, 7, 8, 9, 0, 1, 2], 20 | [4, 5, 6, 7, 8, 9, 0, 1, 2, 3], 21 | [5, 6, 7, 8, 9, 0, 1, 2, 3, 4], 22 | [6, 7, 8, 9, 0, 1, 2, 3, 4, 5], 23 | [7, 8, 9, 0, 1, 2, 3, 4, 5, 6], 24 | [8, 9, 0, 1, 2, 3, 4, 5, 6, 7], 25 | [9, 0, 1, 2, 3, 4, 5, 6, 7, 8], 26 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 27 | ] 28 | ]; 29 | 30 | type AddDigitCarryTable = [ 31 | [ 32 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 33 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 34 | [0, 0, 0, 0, 0, 0, 0, 0, 1, 1], 35 | [0, 0, 0, 0, 0, 0, 0, 1, 1, 1], 36 | [0, 0, 0, 0, 0, 0, 1, 1, 1, 1], 37 | [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], 38 | [0, 0, 0, 0, 1, 1, 1, 1, 1, 1], 39 | [0, 0, 0, 1, 1, 1, 1, 1, 1, 1], 40 | [0, 0, 1, 1, 1, 1, 1, 1, 1, 1], 41 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1] 42 | ], 43 | [ 44 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 45 | [0, 0, 0, 0, 0, 0, 0, 0, 1, 1], 46 | [0, 0, 0, 0, 0, 0, 0, 1, 1, 1], 47 | [0, 0, 0, 0, 0, 0, 1, 1, 1, 1], 48 | [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], 49 | [0, 0, 0, 0, 1, 1, 1, 1, 1, 1], 50 | [0, 0, 0, 1, 1, 1, 1, 1, 1, 1], 51 | [0, 0, 1, 1, 1, 1, 1, 1, 1, 1], 52 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1], 53 | [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 54 | ] 55 | ]; 56 | 57 | type AddDigit< 58 | T extends Digit, 59 | U extends Digit, 60 | Carry extends 0 | 1 = 0 61 | > = AddDigitTable[Carry][T][U]; 62 | 63 | type AddCarryDigit< 64 | T extends Digit, 65 | U extends Digit, 66 | Carry extends 0 | 1 = 0 67 | > = AddDigitCarryTable[Carry][T][U]; 68 | 69 | export type AddDigits< 70 | T extends Digit[], 71 | U extends Digit[], 72 | Carry extends 0 | 1 = 0, 73 | Acc extends Digit[] = [] 74 | > = T extends [...infer R extends Digit[], infer N extends Digit] 75 | ? U extends [...infer S extends Digit[], infer M extends Digit] 76 | ? AddDigits< 77 | R, 78 | S, 79 | AddCarryDigit, 80 | [AddDigit, ...Acc] 81 | > 82 | : AddDigits< 83 | R, 84 | [], 85 | AddCarryDigit, 86 | [AddDigit, ...Acc] 87 | > 88 | : U extends [...infer S extends Digit[], infer M extends Digit] 89 | ? AddDigits< 90 | [], 91 | S, 92 | AddCarryDigit<0, M, Carry>, 93 | [AddDigit<0, M, Carry>, ...Acc] 94 | > 95 | : Carry extends 1 96 | ? [1, ...Acc] 97 | : Acc; 98 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/digits/division.ts: -------------------------------------------------------------------------------- 1 | import { Digit, Digits, TrimZeros } from "../utils"; 2 | import { CompareDigits } from "../compare"; 3 | import { AddDigits } from "./addition"; 4 | import { SubDigits } from "./substraction"; 5 | 6 | export type Rest = T extends [ 7 | Digit, 8 | ...infer R extends Digit[] 9 | ] 10 | ? R 11 | : never; 12 | 13 | type TruncateWith< 14 | T extends Digit[], 15 | U extends Digit[], 16 | Acc extends Digit[] = [] 17 | > = U extends [] 18 | ? [T, Acc] 19 | : T extends [infer D extends Digit, ...infer DR extends Digit[]] 20 | ? TruncateWith, [...Acc, D]> 21 | : [T, Acc]; 22 | 23 | type DivModByDigit< 24 | D extends Digit[], 25 | M extends Digit[], 26 | Mul extends Digit[] = [0], 27 | IterTable extends Digit[] = Digits, 28 | NextMul extends Digit[] = AddDigits, 29 | Comp = CompareDigits 30 | > = IterTable extends [ 31 | infer Iteration extends Digit, 32 | ...infer Next extends Digit[] 33 | ] 34 | ? Comp extends 0 35 | ? { Quotient: Next[0]; Remainder: [0] } 36 | : Comp extends 1 37 | ? DivModByDigit 38 | : { 39 | Quotient: Iteration; 40 | Remainder: SubDigits; 41 | } 42 | : never; 43 | 44 | /** 45 | * compute the long division of a number by a divisor 46 | * @param A the Numerator Cut after M digits 47 | * @param D the Numerator Cut with M first digits 48 | * @param M the Divisor 49 | * @param Q the Quotient 50 | * @see https://en.wikipedia.org/wiki/Long_division#Algorithm_for_arbitrary_base 51 | */ 52 | export type _DivModDigits< 53 | A extends Digit[], 54 | D extends Digit[], 55 | M extends Digit[], 56 | Q extends Digit[] = [] 57 | > = DivModByDigit extends { 58 | Quotient: infer B extends Digit; 59 | Remainder: infer R extends Digit[]; 60 | } 61 | ? A extends [infer A1 extends Digit, ...infer AR extends Digit[]] 62 | ? _DivModDigits, M, [...Q, B]> 63 | : { Quotient: [...Q, B]; Remainder: R } 64 | : never; 65 | 66 | export type DivDigits = TruncateWith< 67 | N, 68 | M 69 | > extends [infer A extends Digit[], infer D extends Digit[]] 70 | ? _DivModDigits["Quotient"] 71 | : never; 72 | 73 | export type ModDigits = TruncateWith< 74 | N, 75 | M 76 | > extends [infer A extends Digit[], infer D extends Digit[]] 77 | ? _DivModDigits["Remainder"] 78 | : never; 79 | 80 | export type DivModDigits = TruncateWith< 81 | N, 82 | M 83 | > extends [infer A extends Digit[], infer D extends Digit[]] 84 | ? _DivModDigits 85 | : never; 86 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/digits/multiply.ts: -------------------------------------------------------------------------------- 1 | import { Digit } from "../utils"; 2 | import { AddDigits } from "./addition"; 3 | import { SubDigits } from "./substraction"; 4 | 5 | export type MulX2 = AddDigits; 6 | export type MulX3 = AddDigits>; 7 | export type MulX4 = MulX2>; 8 | export type MulX5 = AddDigits>; 9 | export type MulX6 = MulX2>; 10 | export type MulX7 = SubDigits, MulX3>; 11 | export type MulX8 = SubDigits, MulX2>; 12 | export type MulX9 = SubDigits, T>; 13 | export type MulX10 = [...T, 0]; 14 | 15 | export type MulByDigit = U extends 0 16 | ? [0] 17 | : U extends 1 18 | ? T 19 | : U extends 2 20 | ? MulX2 21 | : U extends 3 22 | ? MulX3 23 | : U extends 4 24 | ? MulX4 25 | : U extends 5 26 | ? MulX5 27 | : U extends 6 28 | ? MulX6 29 | : U extends 7 30 | ? MulX7 31 | : U extends 8 32 | ? MulX8 33 | : MulX9; 34 | 35 | export type MulDigits< 36 | T extends Digit[], 37 | U extends Digit[], 38 | Acc extends Digit[] = [] 39 | > = U extends [infer N extends Digit, ...infer R extends Digit[]] 40 | ? MulDigits, MulX10>> 41 | : Acc; 42 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/digits/power.ts: -------------------------------------------------------------------------------- 1 | import { Digit, TrimZeros } from "../utils"; 2 | import { _DivModDigits } from "./division"; 3 | import { MulDigits } from "./multiply"; 4 | 5 | export type PowerDigits< 6 | T extends Digit[], 7 | U extends Digit[], 8 | Acc extends Digit[] = [1] 9 | > = U extends [0] 10 | ? [1] 11 | : U extends [1] 12 | ? MulDigits 13 | : U extends [infer UN extends Digit, ...infer UR extends Digit[]] 14 | ? _DivModDigits extends { 15 | Quotient: infer Q extends Digit[]; 16 | Remainder: infer R extends Digit[]; 17 | } 18 | ? TrimZeros extends [0] 19 | ? PowerDigits, TrimZeros, Acc> 20 | : PowerDigits, TrimZeros, MulDigits> 21 | : never 22 | : Acc; 23 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/digits/substraction.ts: -------------------------------------------------------------------------------- 1 | import type { Digit } from "../utils"; 2 | 3 | type SubDigitTable = [ 4 | [ 5 | [0, 9, 8, 7, 6, 5, 4, 3, 2, 1], 6 | [1, 0, 9, 8, 7, 6, 5, 4, 3, 2], 7 | [2, 1, 0, 9, 8, 7, 6, 5, 4, 3], 8 | [3, 2, 1, 0, 9, 8, 7, 6, 5, 4], 9 | [4, 3, 2, 1, 0, 9, 8, 7, 6, 5], 10 | [5, 4, 3, 2, 1, 0, 9, 8, 7, 6], 11 | [6, 5, 4, 3, 2, 1, 0, 9, 8, 7], 12 | [7, 6, 5, 4, 3, 2, 1, 0, 9, 8], 13 | [8, 7, 6, 5, 4, 3, 2, 1, 0, 9], 14 | [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 15 | ], 16 | [ 17 | [9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 18 | [0, 9, 8, 7, 6, 5, 4, 3, 2, 1], 19 | [1, 0, 9, 8, 7, 6, 5, 4, 3, 2], 20 | [2, 1, 0, 9, 8, 7, 6, 5, 4, 3], 21 | [3, 2, 1, 0, 9, 8, 7, 6, 5, 4], 22 | [4, 3, 2, 1, 0, 9, 8, 7, 6, 5], 23 | [5, 4, 3, 2, 1, 0, 9, 8, 7, 6], 24 | [6, 5, 4, 3, 2, 1, 0, 9, 8, 7], 25 | [7, 6, 5, 4, 3, 2, 1, 0, 9, 8], 26 | [8, 7, 6, 5, 4, 3, 2, 1, 0, 9] 27 | ] 28 | ]; 29 | 30 | type SubDigitCarryTable = [ 31 | [ 32 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1], 33 | [0, 0, 1, 1, 1, 1, 1, 1, 1, 1], 34 | [0, 0, 0, 1, 1, 1, 1, 1, 1, 1], 35 | [0, 0, 0, 0, 1, 1, 1, 1, 1, 1], 36 | [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], 37 | [0, 0, 0, 0, 0, 0, 1, 1, 1, 1], 38 | [0, 0, 0, 0, 0, 0, 0, 1, 1, 1], 39 | [0, 0, 0, 0, 0, 0, 0, 0, 1, 1], 40 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 41 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 42 | ], 43 | [ 44 | [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 45 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1], 46 | [0, 0, 1, 1, 1, 1, 1, 1, 1, 1], 47 | [0, 0, 0, 1, 1, 1, 1, 1, 1, 1], 48 | [0, 0, 0, 0, 1, 1, 1, 1, 1, 1], 49 | [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], 50 | [0, 0, 0, 0, 0, 0, 1, 1, 1, 1], 51 | [0, 0, 0, 0, 0, 0, 0, 1, 1, 1], 52 | [0, 0, 0, 0, 0, 0, 0, 0, 1, 1], 53 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] 54 | ] 55 | ]; 56 | 57 | type SubDigit< 58 | T extends Digit, 59 | U extends Digit, 60 | Carry extends 0 | 1 = 0 61 | > = SubDigitTable[Carry][T][U]; 62 | 63 | type SubCarryDigit< 64 | T extends Digit, 65 | U extends Digit, 66 | Carry extends 0 | 1 = 0 67 | > = SubDigitCarryTable[Carry][T][U]; 68 | 69 | export type SubDigits< 70 | T extends Digit[], 71 | U extends Digit[], 72 | Carry extends 0 | 1 = 0, 73 | Acc extends Digit[] = [] 74 | > = T extends [...infer R extends Digit[], infer N extends Digit] 75 | ? U extends [...infer S extends Digit[], infer M extends Digit] 76 | ? SubDigits< 77 | R, 78 | S, 79 | SubCarryDigit, 80 | [SubDigit, ...Acc] 81 | > 82 | : SubDigits< 83 | R, 84 | [], 85 | SubCarryDigit, 86 | [SubDigit, ...Acc] 87 | > 88 | : U extends [...infer S extends Digit[], infer M extends Digit] 89 | ? SubDigits< 90 | [], 91 | S, 92 | SubCarryDigit<0, M, Carry>, 93 | [SubDigit<0, M, Carry>, ...Acc] 94 | > 95 | : Carry extends 1 96 | ? [...Acc, 9] 97 | : Acc; 98 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/division.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ToNumber, 3 | MakeDigitNumber, 4 | FromDigitNumber, 5 | Normalize, 6 | DigitNumber, 7 | Sign, 8 | Num, 9 | ToDigitNumber, 10 | ToString, 11 | MulSign, 12 | Digit, 13 | } from "./utils"; 14 | import { DivDigits, ModDigits, DivModDigits } from "./digits/division"; 15 | 16 | export type DivDigitNumbers< 17 | T extends DigitNumber, 18 | U extends DigitNumber 19 | > = MakeDigitNumber, Sign>, DivDigits, Num>>; 20 | 21 | export type Div< 22 | T extends number | bigint, 23 | U extends number | bigint 24 | > = ToNumber< 25 | FromDigitNumber< 26 | Normalize< 27 | DivDigitNumbers>, ToDigitNumber>> 28 | > 29 | > 30 | >; 31 | 32 | export type ModDigitNumbers< 33 | T extends DigitNumber, 34 | U extends DigitNumber 35 | > = MakeDigitNumber, ModDigits, Num>>; 36 | 37 | export type Mod< 38 | T extends number | bigint, 39 | U extends number | bigint 40 | > = ToNumber< 41 | FromDigitNumber< 42 | Normalize< 43 | ModDigitNumbers>, ToDigitNumber>> 44 | > 45 | > 46 | >; 47 | 48 | export type DivModDigitNumbers< 49 | T extends DigitNumber, 50 | U extends DigitNumber, 51 | DivMod extends { Quotient: Digit[]; Remainder: Digit[] } = DivModDigits< 52 | Num, 53 | Num 54 | > 55 | > = { 56 | Quotient: MakeDigitNumber, Sign>, DivMod["Quotient"]>; 57 | Remainder: MakeDigitNumber, DivMod["Remainder"]>; 58 | }; 59 | 60 | export type DivMod< 61 | T extends number | bigint, 62 | U extends number | bigint, 63 | DivModNumbers extends { 64 | Quotient: DigitNumber; 65 | Remainder: DigitNumber; 66 | } = DivModDigitNumbers>, ToDigitNumber>> 67 | > = { 68 | Quotient: ToNumber>>; 69 | Remainder: ToNumber>>; 70 | }; 71 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/multiply.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ToNumber, 3 | MakeDigitNumber, 4 | FromDigitNumber, 5 | Normalize, 6 | DigitNumber, 7 | Sign, 8 | Num, 9 | ToDigitNumber, 10 | ToString, 11 | MulSign, 12 | } from "./utils"; 13 | import { MulDigits } from "./digits/multiply"; 14 | 15 | export type MulDigitNumbers< 16 | T extends DigitNumber, 17 | U extends DigitNumber 18 | > = MakeDigitNumber, Sign>, MulDigits, Num>>; 19 | 20 | export type Mul< 21 | T extends number | bigint, 22 | U extends number | bigint 23 | > = ToNumber< 24 | FromDigitNumber< 25 | Normalize< 26 | MulDigitNumbers>, ToDigitNumber>> 27 | > 28 | > 29 | >; 30 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/negate.ts: -------------------------------------------------------------------------------- 1 | export type Negate = 2 | `${T}` extends `-${infer U extends number | bigint}` 3 | ? U 4 | : `-${T}` extends `${infer U extends number | bigint}` 5 | ? U 6 | : never; 7 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/numbers.ts: -------------------------------------------------------------------------------- 1 | export type { Abs } from "./absolute"; 2 | export type { Add } from "./addition"; 3 | export type { Sub } from "./substraction"; 4 | export type { Negate } from "./negate"; 5 | export type { Mul } from "./multiply"; 6 | export type { Div, Mod } from "./division"; 7 | export type { Power } from "./power"; 8 | export type { 9 | Compare, 10 | Equal, 11 | NotEqual, 12 | LessThan, 13 | LessThanOrEqual, 14 | GreaterThan, 15 | GreaterThanOrEqual, 16 | Max, 17 | Min, 18 | } from "./compare"; 19 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/power.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ToNumber, 3 | MakeDigitNumber, 4 | FromDigitNumber, 5 | Normalize, 6 | DigitNumber, 7 | Sign, 8 | Num, 9 | ToDigitNumber, 10 | ToString, 11 | Digit, 12 | } from "./utils"; 13 | import { PowerDigits } from "./digits/power"; 14 | 15 | type PowerSign = S extends "-" 16 | ? Num extends [...Digit[], 0 | 2 | 4 | 6 | 8] 17 | ? "" 18 | : "-" 19 | : ""; 20 | 21 | export type PowerDigitNumbers< 22 | T extends DigitNumber, 23 | U extends DigitNumber 24 | > = Sign extends "-" 25 | ? MakeDigitNumber, [0]> 26 | : MakeDigitNumber, U>, PowerDigits, Num>>; 27 | 28 | export type Power< 29 | T extends number | bigint, 30 | U extends number | bigint 31 | > = ToNumber< 32 | FromDigitNumber< 33 | Normalize< 34 | PowerDigitNumbers>, ToDigitNumber>> 35 | > 36 | > 37 | >; 38 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/range.ts: -------------------------------------------------------------------------------- 1 | import { Add } from "./addition"; 2 | import { AddDigits } from "./digits/addition"; 3 | import { Digit, Num, ToDigitNumber, ToString } from "./utils"; 4 | import { Sub } from "./substraction"; 5 | 6 | export type SequenceOfDigits< 7 | T extends number | bigint, 8 | Min extends number | bigint = 0, 9 | MinDigits extends Digit[] = Num>>, 10 | Acc extends Digit[][] = [MinDigits] 11 | > = Acc["length"] extends T 12 | ? Acc 13 | : SequenceOfDigits< 14 | T, 15 | Min, 16 | MinDigits, 17 | [ 18 | ...Acc, 19 | AddDigits>>, MinDigits> 20 | ] 21 | >; 22 | 23 | export type RangeOfDigits< 24 | Min extends number | bigint, 25 | Max extends number | bigint 26 | > = SequenceOfDigits, Min>, Min>; 27 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/substraction.ts: -------------------------------------------------------------------------------- 1 | import { AddDigits } from "./digits/addition"; 2 | import { 3 | DigitNumber, 4 | FromDigitNumber, 5 | InvertSign, 6 | MakeDigitNumber, 7 | Normalize, 8 | Num, 9 | Sign, 10 | ToDigitNumber, 11 | ToNumber, 12 | ToString, 13 | } from "./utils"; 14 | import { CompareDigits } from "./compare"; 15 | import { SubDigits } from "./digits/substraction"; 16 | 17 | type SubDigitNumbers< 18 | T extends DigitNumber, 19 | U extends DigitNumber 20 | > = Sign extends Sign 21 | ? CompareDigits, Num> extends 1 22 | ? MakeDigitNumber, SubDigits, Num>> 23 | : MakeDigitNumber, SubDigits, Num>> 24 | : MakeDigitNumber, AddDigits, Num>>; 25 | 26 | export type Sub< 27 | T extends number | bigint, 28 | U extends number | bigint 29 | > = ToNumber< 30 | FromDigitNumber< 31 | Normalize< 32 | SubDigitNumbers>, ToDigitNumber>> 33 | > 34 | > 35 | >; 36 | -------------------------------------------------------------------------------- /src/internals/numbers/impl/utils.ts: -------------------------------------------------------------------------------- 1 | export type ToNumber = T extends `${infer N extends 2 | | number 3 | | bigint}` 4 | ? N 5 | : never; 6 | 7 | export type ToString = `${T}`; 8 | 9 | export type Digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 10 | export type Digit = Digits[number]; 11 | 12 | export type DigitNumber = { sign: "-" | ""; num: Digit[] }; 13 | export type MakeDigitNumber = { 14 | sign: S; 15 | num: N; 16 | }; 17 | 18 | export type ToDigits< 19 | T extends string, 20 | Acc extends Digit[] = [] 21 | > = T extends `${infer N extends Digit}${infer R}` 22 | ? ToDigits 23 | : Acc; 24 | 25 | export type ToDigitNumber = T extends `-${infer R}` 26 | ? { sign: "-"; num: ToDigits } 27 | : { sign: ""; num: ToDigits }; 28 | 29 | export type FromDigits = T extends [ 30 | infer N extends Digit, 31 | ...infer R 32 | ] 33 | ? FromDigits 34 | : Acc; 35 | 36 | export type Sign = T["sign"]; 37 | export type InvertSign = Sign extends "-" ? "" : "-"; 38 | export type MulSign = S1 extends "-" 39 | ? S2 extends "-" 40 | ? "" 41 | : "-" 42 | : S2 extends "-" 43 | ? "-" 44 | : ""; 45 | 46 | export type Num = T["num"]; 47 | 48 | export type FromDigitNumber = `${Sign}${FromDigits< 49 | Num 50 | >}`; 51 | 52 | export type TrimZeros = T extends [0] 53 | ? [0] 54 | : T extends [0, ...infer R extends Digit[]] 55 | ? TrimZeros 56 | : T; 57 | 58 | export type Normalize< 59 | T extends DigitNumber, 60 | Trim extends Digit[] = TrimZeros> 61 | > = Trim extends [0] 62 | ? MakeDigitNumber<"", Trim> 63 | : MakeDigitNumber, Trim>; 64 | -------------------------------------------------------------------------------- /src/internals/objects/impl/objects.ts: -------------------------------------------------------------------------------- 1 | import { Apply, Call, Fn } from "../../core/Core"; 2 | import { Strings } from "../../strings/Strings"; 3 | import { 4 | Equal, 5 | IsTuple, 6 | Prettify, 7 | Primitive, 8 | UnionToIntersection, 9 | } from "../../helpers"; 10 | 11 | export type Keys = src extends readonly unknown[] 12 | ? { 13 | [key in keyof src]: key; 14 | }[number] extends infer res 15 | ? res extends string 16 | ? Call & keyof src 17 | : res & keyof src 18 | : never 19 | : keyof src; 20 | 21 | export type Values = Keys extends infer keys extends keyof src 22 | ? src[keys] 23 | : never; 24 | 25 | export type FromEntries = 26 | | { 27 | [entry in entries as entry[0]]: entry[1]; 28 | } 29 | // prettifies the name. 30 | | never; 31 | 32 | export type Entries = Keys extends infer keys extends keyof T 33 | ? { 34 | [K in keys]: [K, T[K]]; 35 | }[keys] 36 | : never; 37 | 38 | export type Assign = Prettify< 39 | UnionToIntersection 40 | >; 41 | 42 | export type GetFromPath = RecursiveGet>; 43 | 44 | type ParsePath< 45 | path, 46 | output extends string[] = [], 47 | currentChunk extends string = "" 48 | > = path extends number 49 | ? [`${path}`] 50 | : path extends `${infer first}${infer rest}` 51 | ? first extends "." | "[" | "]" 52 | ? ParsePath< 53 | rest, 54 | [...output, ...(currentChunk extends "" ? [] : [currentChunk])], 55 | "" 56 | > 57 | : ParsePath 58 | : [...output, ...(currentChunk extends "" ? [] : [currentChunk])]; 59 | 60 | type RecursiveGet = Obj extends any 61 | ? pathList extends [infer first, ...infer rest] 62 | ? first extends keyof Obj 63 | ? RecursiveGet 64 | : [first, Obj] extends [`${number}` | "number", readonly any[]] 65 | ? RecursiveGet[number], rest> 66 | : undefined 67 | : Obj 68 | : never; 69 | 70 | export type TransformObjectDeep = type extends 71 | | Function 72 | | Date 73 | ? type 74 | : type extends Map 75 | ? Map, TransformObjectDeep> 76 | : type extends ReadonlyMap 77 | ? ReadonlyMap, TransformObjectDeep> 78 | : type extends WeakMap 79 | ? WeakMap< 80 | Extract, object>, 81 | TransformObjectDeep 82 | > 83 | : type extends Set 84 | ? Set> 85 | : type extends ReadonlySet 86 | ? ReadonlySet> 87 | : type extends WeakSet 88 | ? WeakSet, object>> 89 | : type extends Array 90 | ? IsTuple extends true 91 | ? Call }> 92 | : Array | undefined> 93 | : type extends Promise 94 | ? Promise> 95 | : type extends object 96 | ? Call }> 97 | : Equal extends true 98 | ? unknown 99 | : Partial; 100 | 101 | export type Update = RecursiveUpdate< 102 | obj, 103 | ParsePath, 104 | fnOrValue 105 | >; 106 | 107 | type RecursiveUpdate = obj extends any 108 | ? pathList extends [infer first, ...infer rest] 109 | ? first extends keyof obj 110 | ? { 111 | [K in keyof obj]: Equal extends true 112 | ? RecursiveUpdate 113 | : obj[K]; 114 | } 115 | : [first, obj] extends ["number", readonly any[]] 116 | ? RecursiveUpdate[number], rest, fnOrValue>[] 117 | : Assign< 118 | [ 119 | obj, 120 | { 121 | [K in Extract]: RecursiveUpdate< 122 | {}, 123 | rest, 124 | fnOrValue 125 | >; 126 | } 127 | ] 128 | > 129 | : fnOrValue extends Fn 130 | ? Call, obj> 131 | : fnOrValue 132 | : never; 133 | 134 | export type Create< 135 | pattern, 136 | args extends unknown[] 137 | > = pattern extends infer p extends Fn 138 | ? Apply 139 | : pattern extends Primitive 140 | ? pattern 141 | : pattern extends readonly [any, ...any] 142 | ? { [key in keyof pattern]: Create } 143 | : pattern extends readonly (infer V)[] 144 | ? Create[] 145 | : pattern extends object 146 | ? { [key in keyof pattern]: Create } 147 | : pattern; 148 | 149 | type JoinPath = [ 150 | A 151 | ] extends [never] 152 | ? B 153 | : [B] extends [never] 154 | ? A 155 | : `${A}${Sep}${B}`; 156 | 157 | export type AllPaths = T extends Primitive 158 | ? ParentPath 159 | : unknown extends T 160 | ? JoinPath 161 | : T extends readonly any[] 162 | ? Keys extends infer key extends string | number 163 | ? 164 | | JoinPath 165 | | AllPaths> 166 | : never 167 | : keyof T extends infer key extends keyof T & string 168 | ? key extends any 169 | ? 170 | | JoinPath 171 | | AllPaths> 172 | : never 173 | : ParentPath; 174 | -------------------------------------------------------------------------------- /src/internals/std/Std.ts: -------------------------------------------------------------------------------- 1 | export namespace Std { 2 | export type _Pick = Pick; 3 | export type _Omit = Omit; 4 | export type _Extract = Extract; 5 | export type _Exclude = Exclude; 6 | export type _Uppercase = Uppercase; 7 | export type _Lowercase = Lowercase; 8 | export type _Capitalize = Capitalize; 9 | export type _Uncapitalize = Uncapitalize; 10 | export type _Record = Record; 11 | export type _Readonly = Readonly; 12 | export type _Required = Required; 13 | export type _Partial = Partial; 14 | export type _NonNullable = NonNullable; 15 | } 16 | -------------------------------------------------------------------------------- /src/internals/strings/Strings.ts: -------------------------------------------------------------------------------- 1 | import { Call, ComposeLeft, Fn, PartialApply, unset, _ } from "../core/Core"; 2 | import { Std } from "../std/Std"; 3 | import { Tuples } from "../tuples/Tuples"; 4 | import * as H from "../helpers"; 5 | import * as Impl from "./impl/strings"; 6 | 7 | export namespace Strings { 8 | export type Stringifiable = 9 | | string 10 | | number 11 | | boolean 12 | | bigint 13 | | null 14 | | undefined; 15 | 16 | /** 17 | * Get the length of a string. 18 | * @param args[0] - The string to get the length of. 19 | * @returns The length of the string. 20 | * @warning - 🔥 does not work with emojis since they are multiple characters 🔥 21 | * @example 22 | * ```ts 23 | * type T0 = Call; // 3 24 | * ``` 25 | */ 26 | export type Length = PartialApply; 27 | 28 | /** 29 | * Get the length of a string. 30 | * @param args[0] - The string to get the length of. 31 | * @returns The length of the string. 32 | * @warning - 🔥 does not work with emojis since they are multiple characters 🔥 33 | * @example 34 | * ```ts 35 | * type T0 = Call; // 3 36 | * ``` 37 | */ 38 | export interface LengthFn extends Fn { 39 | return: this["arg0"] extends string ? Impl.Length : never; 40 | } 41 | 42 | /** 43 | * Trim the left side of a string. 44 | * @param args[0] - The string to trim. 45 | * @param Sep - The separator to trim. 46 | * @returns The trimmed string. 47 | * @example 48 | * ```ts 49 | * type T0 = Call; // "abc" 50 | * ``` 51 | */ 52 | export type TrimLeft< 53 | Sep extends string | _ = " ", 54 | Str = unset 55 | > = PartialApply; 56 | 57 | interface TrimLeftFn extends Fn { 58 | return: this["args"] extends [ 59 | infer Sep extends string, 60 | infer Str extends string, 61 | ...any 62 | ] 63 | ? Impl.TrimLeft 64 | : never; 65 | } 66 | 67 | /** 68 | * Trim the right side of a string. 69 | * @param args[0] - The string to trim. 70 | * @param Sep - The separator to trim. 71 | * @returns The trimmed string. 72 | * @example 73 | * ```ts 74 | * type T0 = Call; // "abc" 75 | * ``` 76 | */ 77 | export type TrimRight< 78 | Sep extends string | _ = " ", 79 | Str = unset 80 | > = PartialApply; 81 | 82 | interface TrimRightFn extends Fn { 83 | return: this["args"] extends [ 84 | infer Sep extends string, 85 | infer Str extends string, 86 | ...any 87 | ] 88 | ? Impl.TrimRight 89 | : never; 90 | } 91 | 92 | /** 93 | * Trim a string. 94 | * @param args[0] - The string to trim. 95 | * @param Sep - The separator to trim. 96 | * @returns The trimmed string. 97 | * @example 98 | * ```ts 99 | * type T0 = Call; // "abc" 100 | * ``` 101 | */ 102 | export type Trim = PartialApply< 103 | TrimFn, 104 | [Sep, Str] 105 | >; 106 | 107 | interface TrimFn extends Fn { 108 | return: this["args"] extends [ 109 | infer Sep extends string, 110 | infer Str extends string, 111 | ...any 112 | ] 113 | ? Impl.Trim 114 | : never; 115 | } 116 | 117 | /** 118 | * Replace all instances of a substring in a string. 119 | * @param args[0] - The string to replace. 120 | * @param from - The substring to replace. 121 | * @param to - The substring to replace with. 122 | * @returns The replaced string. 123 | * @example 124 | * ```ts 125 | * type T0 = Call,"a.b.c.d">; // "a/b/c/d" 126 | */ 127 | export type Replace< 128 | from extends string | unset | _ = unset, 129 | to extends string | unset | _ = unset, 130 | str = unset 131 | > = PartialApply; 132 | 133 | interface ReplaceFn extends Fn { 134 | return: this["args"] extends [ 135 | infer From extends string, 136 | infer To extends string, 137 | infer Str, 138 | ...any 139 | ] 140 | ? Call, Str>, H.UnionToTuple> 141 | : never; 142 | } 143 | 144 | /** 145 | * Cut a slice of a string out from a start index to an end index. 146 | * @param args[0] - The string to slice. 147 | * @param start - The start index. 148 | * @param end - The end index. 149 | * @returns The sliced string. 150 | * @warning - 🔥 does not work with emojis since they are multiple characters 🔥 151 | * @example 152 | * ```ts 153 | * type T0 = Call,"1234567890">; // "23456789" 154 | */ 155 | export type Slice< 156 | start extends number | unset | _ = unset, 157 | end extends number | unset | _ = unset 158 | > = ComposeLeft< 159 | [Strings.Split<"">, Tuples.Take, Tuples.Drop, Tuples.Join<"">] 160 | >; 161 | 162 | /** 163 | * Split a string into a tuple of strings. 164 | * @param args[0] - The string to split. 165 | * @param sep - The separator to split the string with. 166 | * @returns The split string. 167 | * @warning - 🔥 using an empty sep with emojis in the string will destroy the emoji 🔥 168 | * @example 169 | * ```ts 170 | * type T0 = Call,"a,b,c">; // ["a","b","c"] 171 | * ``` 172 | */ 173 | export type Split< 174 | Sep extends string | unset | _ = unset, 175 | Str extends string | unset | _ = unset 176 | > = PartialApply; 177 | 178 | export interface SplitFn extends Fn { 179 | return: this["args"] extends [infer Sep extends string, infer Str, ...any] 180 | ? Impl.Split 181 | : never; 182 | } 183 | 184 | /** 185 | * Repeat a string a number of times. 186 | * @param args[0] - The string to repeat. 187 | * @param times - The number of times to repeat the string. 188 | * @returns The repeated string. 189 | * @example 190 | * ```ts 191 | * type T0 = Call,"Hello! ">; // "Hello! Hello! Hello! " 192 | * ``` 193 | */ 194 | export type Repeat< 195 | Times extends number | _ | unset = unset, 196 | Str extends string | _ | unset = unset 197 | > = PartialApply; 198 | 199 | interface RepeatFn extends Fn { 200 | return: this["args"] extends [ 201 | infer Times extends number, 202 | infer Str extends string 203 | ] 204 | ? Impl.Repeat 205 | : never; 206 | } 207 | 208 | /** 209 | * Check if a string starts with a substring. 210 | * @param args[0] - The string to check. 211 | * @param str - The substring to check for. 212 | * @returns Whether the string starts with the substring. 213 | * @example 214 | * ```ts 215 | * type T0 = Call,"abcdef">; // true 216 | * type T1 = Call,"defabc">; // false 217 | * ``` 218 | */ 219 | export type StartsWith< 220 | Start extends string | _ | unset = unset, 221 | Str extends string | _ | unset = unset 222 | > = PartialApply; 223 | 224 | interface StartsWithFn extends Fn { 225 | return: this["args"] extends [infer Start extends string, infer Str] 226 | ? Str extends `${Start}${string}` 227 | ? true 228 | : false 229 | : never; 230 | } 231 | 232 | /** 233 | * Check if a string ends with a substring. 234 | * @param args[0] - The string to check. 235 | * @param str - The substring to check for. 236 | * @returns Whether the string ends with the substring. 237 | * @example 238 | * ```ts 239 | * type T0 = Call,"abcdef">; // false 240 | * type T1 = Call,"defabc">; // true 241 | * ``` 242 | */ 243 | export type EndsWith< 244 | End extends string | _ | unset = unset, 245 | Str extends string | _ | unset = unset 246 | > = PartialApply; 247 | 248 | interface EndsWithFn extends Fn { 249 | return: this["args"] extends [infer End extends string, infer Str] 250 | ? Str extends `${string}${End}` 251 | ? true 252 | : false 253 | : never; 254 | } 255 | 256 | /** 257 | * Split a string into a tuple of each character. 258 | * @param args[0] - The string to split. 259 | * @returns The splited string. 260 | * @warning - 🔥 does not work with emojis since they are multiple characters 🔥 261 | * @example 262 | * ```ts 263 | * type T0 = Call; // ["a","b","c"] 264 | * ``` 265 | */ 266 | export interface ToTuple extends Fn { 267 | return: Impl.StringToTuple; 268 | } 269 | 270 | /** 271 | * Convert a string to a number or bigint. 272 | * @param args[0] - The string to convert. 273 | * @returns The converted number or bigint. 274 | * @example 275 | * ```ts 276 | * type T0 = Call; // 123 277 | * type T1 = Call; // 12367543547677078675456656790n 278 | * ``` 279 | */ 280 | export interface ToNumber extends Fn { 281 | return: this["arg0"] extends `${infer n extends number | bigint}` 282 | ? n 283 | : never; 284 | } 285 | 286 | /** 287 | * Convert a stringifiable literal to a string. 288 | * @param args[0] - The stringifiable literal to convert. 289 | * @returns The converted string. 290 | * @example 291 | * ```ts 292 | * type T0 = Call; // "123" 293 | * type T1 = Call; // "true" 294 | * type T2 = Call; // "null" 295 | * ``` 296 | */ 297 | export interface ToString extends Fn { 298 | return: `${Extract}`; 299 | } 300 | 301 | /** 302 | * Prepend a string to another string. 303 | * @param args[0] - The string to be prepended to. 304 | * @param str - The string to prepend. 305 | * @returns The prepended string. 306 | * @example 307 | * ```ts 308 | * type T0 = Call,"def">; // "abcdef" 309 | * ``` 310 | */ 311 | export type Prepend< 312 | Start extends string | _ | unset = unset, 313 | Str extends string | _ | unset = unset 314 | > = PartialApply; 315 | 316 | interface PrependFn extends Fn { 317 | return: `${Extract}${Extract< 318 | this["arg1"], 319 | Strings.Stringifiable 320 | >}`; 321 | } 322 | 323 | /** 324 | * Append a string to another string. 325 | * @param args[0] - The string to be appended to. 326 | * @param str - The string to append. 327 | * @returns The appended string. 328 | * @example 329 | * ```ts 330 | * type T0 = Call,"def">; // "defabc" 331 | * ``` 332 | */ 333 | export type Append< 334 | End extends string | _ | unset = unset, 335 | Str extends string | _ | unset = unset 336 | > = PartialApply; 337 | 338 | interface AppendFn extends Fn { 339 | return: `${Extract}${Extract< 340 | this["arg0"], 341 | Strings.Stringifiable 342 | >}`; 343 | } 344 | 345 | /** 346 | * Transform a string to uppercase. 347 | * @param args[0] - The string to transform. 348 | * @returns The transformed string. 349 | * @example 350 | * ```ts 351 | * type T0 = Call; // "ABC" 352 | * ``` 353 | */ 354 | export interface Uppercase extends Fn { 355 | return: Std._Uppercase>; 356 | } 357 | 358 | /** 359 | * Transform a string to lowercase. 360 | * @param args[0] - The string to transform. 361 | * @returns The transformed string. 362 | * @example 363 | * ```ts 364 | * type T0 = Call; // "abc" 365 | * ``` 366 | */ 367 | export interface Lowercase extends Fn { 368 | return: Std._Lowercase>; 369 | } 370 | 371 | /** 372 | * Capitalize a string. 373 | * @param args[0] - The string to capitalize. 374 | * @returns The capitalized string. 375 | * @example 376 | * ```ts 377 | * type T0 = Call; // "Abc" 378 | * ``` 379 | */ 380 | export interface Capitalize extends Fn { 381 | return: Std._Capitalize>; 382 | } 383 | 384 | /** 385 | * Uncapitalize a string. 386 | * @param args[0] - The string to uncapitalize. 387 | * @returns The uncapitalized string. 388 | * @example 389 | * ```ts 390 | * type T0 = Call; // "addTop" 391 | * ``` 392 | */ 393 | export interface Uncapitalize extends Fn { 394 | return: Std._Uncapitalize>; 395 | } 396 | 397 | /** 398 | * Convert a string to snake case. 399 | * @param args[0] - The string to convert. 400 | * @returns The converted string. 401 | * @example 402 | * ```ts 403 | * type T0 = Call; // "add_top" 404 | * ``` 405 | */ 406 | export interface SnakeCase extends Fn { 407 | return: H.SnakeCase; 408 | } 409 | 410 | /** 411 | * Convert a string to camel case. 412 | * @param args[0] - The string to convert. 413 | * @returns The converted string. 414 | * @example 415 | * ```ts 416 | * type T0 = Call; // "addTop" 417 | * ``` 418 | */ 419 | export interface CamelCase extends Fn { 420 | return: H.CamelCase; 421 | } 422 | 423 | /** 424 | * Convert a string to kebab case. 425 | * @param args[0] - The string to convert. 426 | * @returns The converted string. 427 | * @example 428 | * ```ts 429 | * type T0 = Call; // "add-top" 430 | * type T1 = Call; // "add-top" 431 | * type T2 = Call; // "add-top" 432 | * ``` 433 | */ 434 | export interface KebabCase extends Fn { 435 | return: H.KebabCase; 436 | } 437 | 438 | /** 439 | * Compare two strings. (only works with ascii extended characters) 440 | * @param args[0] - The first string to compare. 441 | * @param args[1] - The second string to compare. 442 | * @n1 - The first string to compare or _. 443 | * @n2 - The second string to compare or _. 444 | * @returns The result of the comparison. 445 | * @example 446 | * ```ts 447 | * type T0 = Call; // -1 448 | * type T1 = Call; // 1 449 | * type T2 = Call; // 0 450 | * ``` 451 | */ 452 | export type Compare< 453 | n1 extends string | _ | unset = unset, 454 | n2 extends string | _ | unset = unset 455 | > = PartialApply; 456 | 457 | interface CompareFn extends Fn { 458 | return: this["args"] extends [ 459 | infer a extends string, 460 | infer b extends string, 461 | ...any 462 | ] 463 | ? Impl.Compare 464 | : never; 465 | } 466 | 467 | /** 468 | * Check if a string is lexically less than another string. (only works with ascii extended characters) 469 | * @param args[0] - The first string to compare. 470 | * @param args[1] - The second string to compare. 471 | * @n1 - The first string to compare or _. 472 | * @n2 - The second string to compare or _. 473 | * @returns True if the first string is lexically less than the second string, false otherwise. 474 | * @example 475 | * ```ts 476 | * type T0 = Call; // true 477 | * type T1 = Call; // false 478 | * type T2 = Call; // false 479 | * ``` 480 | */ 481 | export type LessThan< 482 | n1 extends string | _ | unset = unset, 483 | n2 extends string | _ | unset = unset 484 | > = PartialApply; 485 | 486 | interface LessThanFn extends Fn { 487 | return: this["args"] extends [ 488 | infer a extends string, 489 | infer b extends string, 490 | ...any 491 | ] 492 | ? Impl.LessThan 493 | : never; 494 | } 495 | 496 | /** 497 | * Check if a string is lexically less than or equal to another string. (only works with ascii extended characters) 498 | * @param args[0] - The first string to compare. 499 | * @param args[1] - The second string to compare. 500 | * @n1 - The first string to compare or _. 501 | * @n2 - The second string to compare or _. 502 | * @returns True if the first string is lexically less than or equal to the second string, false otherwise. 503 | * @example 504 | * ```ts 505 | * type T0 = Call; // true 506 | * type T1 = Call; // false 507 | * type T2 = Call; // true 508 | */ 509 | export type LessThanOrEqual< 510 | n1 extends string | _ | unset = unset, 511 | n2 extends string | _ | unset = unset 512 | > = PartialApply< 513 | LessThanOrEqualFn, 514 | n2 extends unset ? [unset, n1] : [n1, n2] 515 | >; 516 | 517 | interface LessThanOrEqualFn extends Fn { 518 | return: this["args"] extends [ 519 | infer a extends string, 520 | infer b extends string, 521 | ...any 522 | ] 523 | ? Impl.LessThanOrEqual 524 | : never; 525 | } 526 | 527 | /** 528 | * Check if a string is lexically greater than another string. (only works with ascii extended characters) 529 | * @param args[0] - The first string to compare. 530 | * @param args[1] - The second string to compare. 531 | * @n1 - The first string to compare or _. 532 | * @n2 - The second string to compare or _. 533 | * @returns True if the first string is lexically greater than the second string, false otherwise. 534 | * @example 535 | * ```ts 536 | * type T0 = Call; // false 537 | * type T1 = Call; // true 538 | * type T2 = Call; // false 539 | * ``` 540 | */ 541 | export type GreaterThan< 542 | n1 extends string | _ | unset = unset, 543 | n2 extends string | _ | unset = unset 544 | > = PartialApply; 545 | 546 | interface GreaterThanFn extends Fn { 547 | return: this["args"] extends [ 548 | infer a extends string, 549 | infer b extends string, 550 | ...any 551 | ] 552 | ? Impl.GreaterThan 553 | : never; 554 | } 555 | 556 | /** 557 | * Check if a string is lexically greater than or equal to another string. (only works with ascii extended characters) 558 | * @param args[0] - The first string to compare. 559 | * @param args[1] - The second string to compare. 560 | * @n1 - The first string to compare or _. 561 | * @n2 - The second string to compare or _. 562 | * @returns True if the first string is lexically greater than or equal to the second string, false otherwise. 563 | * @example 564 | * ```ts 565 | * type T0 = Call; // false 566 | * type T1 = Call; // true 567 | * type T2 = Call; // true 568 | * ``` 569 | */ 570 | export type GreaterThanOrEqual< 571 | n1 extends string | _ | unset = unset, 572 | n2 extends string | _ | unset = unset 573 | > = PartialApply< 574 | GreaterThanOrEqualFn, 575 | n2 extends unset ? [unset, n1] : [n1, n2] 576 | >; 577 | 578 | interface GreaterThanOrEqualFn extends Fn { 579 | return: this["args"] extends [ 580 | infer a extends string, 581 | infer b extends string, 582 | ...any 583 | ] 584 | ? Impl.GreaterThanOrEqual 585 | : never; 586 | } 587 | } 588 | -------------------------------------------------------------------------------- /src/internals/strings/impl/compare.ts: -------------------------------------------------------------------------------- 1 | import { Call } from "../../core/Core"; 2 | import { Numbers } from "../../numbers/Numbers"; 3 | import { StringToTuple } from "./split"; 4 | import { Equal as _Equal } from "../../helpers"; 5 | 6 | // prettier-ignore 7 | type ascii = { 8 | " ": 32; "!": 33; '"': 34; "#": 35; $: 36; "%": 37; "&": 38; "'": 39; 9 | "(": 40; ")": 41; "*": 42; "+": 43; ",": 44; "-": 45; ".": 46; "/": 47; "0": 48; "1": 49; 10 | "2": 50; "3": 51; "4": 52; "5": 53; "6": 54; "7": 55; "8": 56; "9": 57; ":": 58; ";": 59; 11 | "<": 60; "=": 61; ">": 62; "?": 63; "@": 64; A: 65; B: 66; C: 67; D: 68; E: 69; 12 | F: 70; G: 71; H: 72; I: 73; J: 74; K: 75; L: 76; M: 77; N: 78; O: 79; 13 | P: 80; Q: 81; R: 82; S: 83; T: 84; U: 85; V: 86; W: 87; X: 88; Y: 89; 14 | Z: 90; "[": 91; "\\": 92; "]": 93; "^": 94; _: 95; "`": 96; a: 97; b: 98; c: 99; 15 | d: 100; e: 101; f: 102; g: 103; h: 104; i: 105; j: 106; k: 107; l: 108; m: 109; 16 | n: 110; o: 111; p: 112; q: 113; r: 114; s: 115; t: 116; u: 117; v: 118; w: 119; 17 | x: 120; y: 121; z: 122; "{": 123; "|": 124; "}": 125; "~": 126; 18 | é: 130; â: 131; ä: 132; à: 133; å: 134; ç: 135; ê: 136; ë: 137; è: 138; ï: 139; 19 | î: 140; ì: 141; Ä: 142; Å: 143; É: 144; æ: 145; Æ: 146; ô: 147; ö: 148; ò: 149; 20 | û: 150; ù: 151; ÿ: 152; Ö: 153; Ü: 154; ø: 155; "£": 156; Ø: 157; "×": 158; ƒ: 159; 21 | á: 160; í: 161; ó: 162; ú: 163; ñ: 164; Ñ: 165; ª: 166; º: 167; "¿": 168; "®": 169; 22 | "½": 171; "¼": 172; "¡": 173; "«": 174; "»": 175; "░": 176; "▒": 177; "▓": 178; "│": 179; 23 | "┤": 180; Á: 181; Â: 182; À: 183; "©": 184; "╣": 185; "║": 186; "╗": 187; "╝": 188; "¢": 189; 24 | "¥": 190; "┐": 191; "└": 192; "┴": 193; "┬": 194; "├": 195; "─": 196; "┼": 197; ã: 198; Ã: 199; 25 | "╚": 200; "╔": 201; "╩": 202; "╦": 203; "╠": 204; "═": 205; "╬": 206; "¤": 207; ð: 208; Ð: 209; 26 | Ê: 210; Ë: 211; È: 212; ı: 213; Í: 214; Î: 215; Ï: 216; "┘": 217; "┌": 218; "█": 219; 27 | "▄": 220; "¦": 221; Ì: 222; "▀": 223; Ó: 224; ß: 225; Ô: 226; Ò: 227; õ: 228; Õ: 229; 28 | µ: 230; þ: 231; Þ: 232; Ú: 233; Û: 234; Ù: 235; ý: 236; Ý: 237; "¯": 238; "´": 239; 29 | "¬": 240; "±": 241; "‗": 242; "¾": 243; "¶": 244; "§": 245; "÷": 246; "¸": 247; "°": 248; "¨": 249; 30 | "•": 250; "¹": 251; "³": 252; "²": 253; "■": 254; 31 | }; 32 | 33 | type CharacterCompare< 34 | Char1 extends string, 35 | Char2 extends string 36 | > = Char1 extends Char2 37 | ? 0 38 | : Char1 extends keyof ascii 39 | ? Char2 extends keyof ascii 40 | ? Call 41 | : 1 42 | : -1; 43 | 44 | type CharactersCompare = T extends [ 45 | infer N1 extends string, 46 | ...infer R1 extends string[] 47 | ] 48 | ? U extends [infer N2 extends string, ...infer R2 extends string[]] 49 | ? CharacterCompare extends 0 50 | ? CharactersCompare 51 | : CharacterCompare 52 | : 1 53 | : U extends [string, ...string[]] 54 | ? -1 55 | : 0; 56 | 57 | export type Compare = _Equal< 58 | T, 59 | U 60 | > extends true 61 | ? 0 62 | : CharactersCompare, StringToTuple>; 63 | 64 | export type LessThan = Compare< 65 | T, 66 | U 67 | > extends -1 68 | ? true 69 | : false; 70 | 71 | export type LessThanOrEqual = Compare< 72 | T, 73 | U 74 | > extends 1 75 | ? false 76 | : true; 77 | 78 | export type Equal = _Equal; 79 | 80 | export type NotEqual = _Equal< 81 | T, 82 | U 83 | > extends true 84 | ? false 85 | : true; 86 | 87 | export type GreaterThan = Compare< 88 | T, 89 | U 90 | > extends 1 91 | ? true 92 | : false; 93 | 94 | export type GreaterThanOrEqual = Compare< 95 | T, 96 | U 97 | > extends -1 98 | ? false 99 | : true; 100 | -------------------------------------------------------------------------------- /src/internals/strings/impl/length.ts: -------------------------------------------------------------------------------- 1 | // Original impl https://gist.github.com/sno2/7dac868ec6d11abb75250ce5e2b36041 2 | 3 | import { Add } from "../../numbers/impl/addition"; 4 | import { StringIterator as StrIter } from "./utils"; 5 | 6 | // Implementation of the following algorithm: 7 | // 8 | // function lengthUp(s, length = 0, it = It.init()) { 9 | // const doubleIt = It.double(it); 10 | // let rest = s.slice(It.size(doubleIt)); 11 | // if (rest) { 12 | // if (It.size(it) === 12) { 13 | // return lengthDown(rest, length + It.value(doubleIt), doubleIt); 14 | // } 15 | // return lengthUp(rest, length + It.value(doubleIt), doubleIt); 16 | // } 17 | // rest = s.slice(It.size(it)); 18 | // if (rest) { 19 | // return lengthUp(rest, length + It.value(it), it); 20 | // } 21 | // return lengthDown(s, length, It.prev(it)); 22 | // } 23 | // 24 | // function lengthDown(s, length, it) { 25 | // if(it) { 26 | // const rest = s.slice(It.size(it)); 27 | // if (rest) { 28 | // return lengthDown(rest, length + It.value(it), it); 29 | // } 30 | // return lengthDown(s, length, It.prev(it)); 31 | // } 32 | // return length; 33 | // } 34 | 35 | type LengthUp< 36 | Str extends string, 37 | Length extends number | bigint = 0, 38 | It extends StrIter.Iterator = StrIter.Init 39 | > = StrIter.Double extends infer $DoubleIt extends StrIter.Iterator 40 | ? StrIter.CutAt extends `${infer $Rest}` 41 | ? StrIter.Size extends 12 // 2^13 is the last block size within the complexity limit 42 | ? LengthDown<$Rest, Add>, $DoubleIt> 43 | : LengthUp<$Rest, Add>, $DoubleIt> 44 | : StrIter.CutAt extends `${infer $Rest}` 45 | ? LengthUp<$Rest, Add>, It> 46 | : LengthDown> 47 | : never; 48 | 49 | type LengthDown< 50 | Str extends string, 51 | Length extends number | bigint, 52 | It 53 | > = It extends StrIter.Iterator 54 | ? StrIter.CutAt extends `${infer $Rest}` 55 | ? LengthDown<$Rest, Add>, It> 56 | : LengthDown> 57 | : Length; 58 | 59 | export type Length = T extends "" ? 0 : LengthUp; 60 | -------------------------------------------------------------------------------- /src/internals/strings/impl/repeat.ts: -------------------------------------------------------------------------------- 1 | import { DivMod } from "../../numbers/impl/division"; 2 | import { Sub } from "../../numbers/impl/substraction"; 3 | 4 | // The below algorithm is based on the following one : 5 | // 6 | // const repeat = (s: string, count: number, acc: string = ''): string => { 7 | // if (count === 0) { 8 | // return acc; 9 | // } else if (count % 2 === 0) { 10 | // return repeat(s + s, count / 2, acc); 11 | // } else { 12 | // return repeat(s, count - 1, acc + s); 13 | // } 14 | // } 15 | 16 | type RepeatX2 = `${T}${T}`; 17 | 18 | export type Repeat< 19 | T extends string, 20 | N extends number, 21 | Acc extends string = "", 22 | Calc extends { Quotient: number; Remainder: number } = DivMod 23 | > = N extends 0 24 | ? Acc 25 | : N extends 1 26 | ? `${Acc}${T}` 27 | : Calc["Remainder"] extends 0 28 | ? Repeat, Calc["Quotient"], Acc> 29 | : Repeat, `${Acc}${T}`>; 30 | -------------------------------------------------------------------------------- /src/internals/strings/impl/replace.ts: -------------------------------------------------------------------------------- 1 | import { Fn } from "../../core/Core"; 2 | 3 | export type Replace< 4 | Str, 5 | From extends string, 6 | To extends string 7 | > = Str extends string 8 | ? Str extends `${infer Before}${From}${infer After}` 9 | ? Replace<`${Before}${To}${After}`, From, To> 10 | : Str 11 | : Str; 12 | 13 | export interface ReplaceReducer extends Fn { 14 | return: this["args"] extends [ 15 | infer Str extends string, 16 | infer From extends string, 17 | ...any 18 | ] 19 | ? Replace 20 | : never; 21 | } 22 | -------------------------------------------------------------------------------- /src/internals/strings/impl/split.ts: -------------------------------------------------------------------------------- 1 | import * as H from "../../helpers"; 2 | 3 | type ConcatSplits< 4 | Parts extends string[], 5 | Seps extends string[], 6 | Acc extends string[] = [] 7 | > = Parts extends [infer First extends string, ...infer Rest extends string[]] 8 | ? ConcatSplits]> 9 | : Acc; 10 | 11 | type SplitManySep< 12 | Str extends string, 13 | Sep extends string[], 14 | Acc extends string[] = [] 15 | > = Sep extends [ 16 | infer FirstSep extends string, 17 | ...infer RestSep extends string[] 18 | ] 19 | ? ConcatSplits, RestSep> 20 | : [Str, ...Acc]; 21 | 22 | /** 23 | * Split a string into a tuple. 24 | * @param Str - The string to split. 25 | * @param Sep - The separator to split on, can be a union of strings of more than one character. 26 | * @returns The tuple of each split. if sep is an empty string, returns a tuple of each character. 27 | */ 28 | export type Split< 29 | Str, 30 | Sep extends string, 31 | Seps = H.UnionToTuple 32 | > = Seps extends string[] 33 | ? Str extends string 34 | ? SplitManySep 35 | : [] 36 | : []; 37 | 38 | /** 39 | * Split a string into a tuple with each character. 40 | * @param Str - The string to split. 41 | * @returns The tuple of each character. 42 | */ 43 | export type StringToTuple = Str extends string 44 | ? Str extends `${infer First}${infer Rest}` 45 | ? StringToTuple 46 | : Acc 47 | : []; 48 | -------------------------------------------------------------------------------- /src/internals/strings/impl/strings.ts: -------------------------------------------------------------------------------- 1 | export * from "./split"; 2 | export * from "./trim"; 3 | export * from "./replace"; 4 | export * from "./repeat"; 5 | export * from "./compare"; 6 | export * from "./length"; 7 | -------------------------------------------------------------------------------- /src/internals/strings/impl/trim.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Trim the left side of a string. 3 | * @param Str - The string to trim. 4 | * @param Sep - The separator to trim. 5 | * @returns The trimmed string. 6 | */ 7 | export type TrimLeft< 8 | Str, 9 | Sep extends string 10 | > = Str extends `${Sep}${infer Rest}` ? TrimLeft : Str; 11 | 12 | /** 13 | * Trim the right side of a string. 14 | * @param Str - The string to trim. 15 | * @param Sep - The separator to trim. 16 | * @returns The trimmed string. 17 | */ 18 | export type TrimRight< 19 | Str, 20 | Sep extends string 21 | > = Str extends `${infer Rest}${Sep}` ? TrimRight : Str; 22 | 23 | /** 24 | * Trim a string. 25 | * @param Str - The string to trim. 26 | * @param Sep - The separator to trim. 27 | * @returns The trimmed string. 28 | */ 29 | export type Trim = TrimLeft, Sep>; 30 | -------------------------------------------------------------------------------- /src/internals/strings/impl/utils.ts: -------------------------------------------------------------------------------- 1 | import { Add } from "../../numbers/impl/addition"; 2 | import { Mul } from "../../numbers/impl/multiply"; 3 | 4 | export namespace StringIterator { 5 | type Iter = [string, number | bigint]; 6 | /** 7 | * Iterator over the string is a linked list of [string, number] pairs 8 | * where the string is the pattern to match any character in the string 'N' times 9 | * and the number is the value 'N' of the pattern 10 | * the linked list allows to easily add iterate and reverse iterate 11 | */ 12 | export type Iterator = [Iter, ...Iter[]]; 13 | /** 14 | * The initial iterator is a list of one element 15 | * 16 | * @description we need to prefix the pattern with '$' 17 | * 18 | * to avoid typescript to merge multiple patterns into one 19 | */ 20 | export type Init = [[`$${string}`, 1]]; 21 | /** 22 | * The string is the pattern to match any character in the string 'N' times 23 | * 24 | * @param It - the iterator to get the string from 25 | * @returns the string 26 | */ 27 | export type String = It[0][0]; 28 | /** 29 | * The value is the number 'N' of the pattern 30 | * 31 | * @param It - the iterator to get the value from 32 | * @returns the value 33 | */ 34 | export type Value = It[0][1]; 35 | /** 36 | * The size is the number of elements in the linked list 37 | * 38 | * @param It - the iterator to get the size from 39 | * @returns the size 40 | */ 41 | export type Size = It["length"]; 42 | /** 43 | * Get the next iterator 44 | * 45 | * @param It - the iterator to get the next iterator from 46 | * @returns the next iterator 47 | */ 48 | export type Next = [ 49 | [`${String}${string}`, Add, 1>], 50 | ...It 51 | ]; 52 | /** 53 | * Get the previous iterator 54 | * @param It - the iterator to get the previous iterator from 55 | * @returns the previous iterator 56 | */ 57 | export type Prev = It extends [ 58 | unknown, 59 | ...infer Rest extends Iterator 60 | ] 61 | ? Rest 62 | : undefined; 63 | /** 64 | * Double the iterator to match any character in the string '2N' times 65 | * This allows the algorithm to be O(log(N)) instead of O(N) 66 | * 67 | * @description we need to prefix the pattern with '$' and suffix it with '_' 68 | * to avoid typescript to merge multiple patterns into one 69 | * 70 | * @param It - the iterator to double 71 | * @returns the doubled iterator 72 | */ 73 | export type Double = 74 | `${String}_` extends `$${infer pattern}` 75 | ? `${String}${pattern}` extends `${infer double}_` 76 | ? [[double, Mul, 2>], ...It] 77 | : never 78 | : never; 79 | 80 | /** 81 | * Cut the string at the iterator 82 | * 83 | * @param T - the string to cut 84 | * @param It - the iterator to cut at 85 | * @returns the rest of the string 86 | */ 87 | export type CutAt< 88 | T extends string, 89 | It extends Iterator 90 | > = `$${T}` extends `${String}${infer $Rest}` ? $Rest : undefined; 91 | } 92 | -------------------------------------------------------------------------------- /src/internals/unions/Unions.ts: -------------------------------------------------------------------------------- 1 | import { Call, Fn, PartialApply, unset, _ } from "../core/Core"; 2 | import { UnionToIntersection, UnionToTuple } from "../helpers"; 3 | import { Std } from "../std/Std"; 4 | import { Tuples } from "../tuples/Tuples"; 5 | 6 | export namespace Unions { 7 | export type Extract< 8 | unionOrExtracted = unset, 9 | extracted = unset 10 | > = PartialApply< 11 | ExtractFn, 12 | extracted extends unset 13 | ? [unset, unionOrExtracted] 14 | : [unionOrExtracted, extracted] 15 | >; 16 | 17 | interface ExtractFn extends Fn { 18 | return: Std._Extract; 19 | } 20 | 21 | type ExtractByImpl = union extends any 22 | ? Call extends true 23 | ? union 24 | : never 25 | : never; 26 | 27 | export interface ExtractBy extends Fn { 28 | return: ExtractByImpl; 29 | } 30 | 31 | export type Exclude = PartialApply< 32 | ExcludeFn, 33 | excluded extends unset 34 | ? [unset, unionOrExcluded] 35 | : [unionOrExcluded, excluded] 36 | >; 37 | 38 | interface ExcludeFn extends Fn { 39 | return: Std._Exclude; 40 | } 41 | 42 | type ExcludeByImpl = union extends any 43 | ? Call extends true 44 | ? never 45 | : union 46 | : never; 47 | 48 | export interface ExcludeBy extends Fn { 49 | return: ExcludeByImpl; 50 | } 51 | 52 | type MapImpl = union extends any 53 | ? Call 54 | : never; 55 | 56 | export type Map = PartialApply; 57 | interface MapFn extends Fn { 58 | return: this["args"] extends [infer fn extends Fn, infer u] 59 | ? MapImpl 60 | : never; 61 | } 62 | 63 | /** 64 | * `Unions.Range` takes a `start` and an `end` integer and produces 65 | * a union containing integer ranging from `start` to `end` 66 | * @param start - the start of the range (included) 67 | * @param end - the end of the range (included) 68 | * @returns a union of integers 69 | * @example 70 | * ```ts 71 | * type T0 = Call, 7>; // 3 | 4 | 5 | 6 | 7 72 | * type T1 = Call, 5>; // 5 | 6 | 7 | 8 | 9 | 10 73 | * type T3 = Call, 5>; // -2 | 1 | 0 | 1 | 2 74 | * type T4 = Call, 5>; // -5 | -4 | -3 | -2 75 | * ``` 76 | */ 77 | export type Range< 78 | start extends number | _ | unset = unset, 79 | end extends number | _ | unset = unset 80 | > = PartialApply; 81 | 82 | interface RangeFn extends Fn { 83 | return: this["args"] extends [ 84 | infer start extends number, 85 | infer end extends number 86 | ] 87 | ? Call>[number] 88 | : never; 89 | } 90 | 91 | /** 92 | * `Unions.ToTuple` turns a union type into a tuple. 93 | * Warning: the ordering of the output tuple is not stable. 94 | * @param union - any union type. 95 | * @returns a tuple containing each member of this union type. 96 | * @example 97 | * ```ts 98 | * type T0 = Call; // [1, 2, 3] 99 | * ``` 100 | */ 101 | export type ToTuple = PartialApply; 102 | 103 | interface ToTupleFn extends Fn { 104 | return: this["args"] extends [infer union, ...any] 105 | ? UnionToTuple 106 | : never; 107 | } 108 | 109 | /** 110 | * `Unions.NonNullable` excludes null and undefined from the union type. 111 | * @param union - any union type. 112 | * @returns a union which is excluded by null and undefined. 113 | * @example 114 | * ```ts 115 | * type T0 = Call; // 1 | "a" 116 | * type T1 = Pipe<"a" | 1 | null | undefined, [U.NonNullable]>; // 1 | "a" 117 | * type T2 = Call>; // 1 | "a" 118 | * ``` 119 | */ 120 | export type NonNullable = PartialApply; 121 | 122 | interface NonNullableFn extends Fn { 123 | return: this["arg0"] extends infer union ? Std._NonNullable : never; 124 | } 125 | 126 | /** 127 | * `Unions.ToIntersection` turns a union type into an intersection type. 128 | * @param union - any union type. 129 | * @returns an intersection of all member of the union 130 | * @example 131 | * ```ts 132 | * type T0 = Call; // {a: string} & {b: number} 133 | * ``` 134 | */ 135 | export type ToIntersection = PartialApply< 136 | ToIntersectionFn, 137 | [union] 138 | >; 139 | 140 | interface ToIntersectionFn extends Fn { 141 | return: this["args"] extends [infer union, ...any] 142 | ? UnionToIntersection 143 | : never; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /test/booleans.test.ts: -------------------------------------------------------------------------------- 1 | import { Call, Tuples, Booleans, T, _ } from "../src/index"; 2 | import { Equal, Expect } from "../src/internals/helpers"; 3 | 4 | describe("Booleans", () => { 5 | describe("And", () => { 6 | it("can be called without any pre-filled arguments", () => { 7 | type res1 = Call, [true, true, false]>; 8 | // ^? 9 | type test1 = Expect>; 10 | }); 11 | 12 | it("can be called with one pre-filled argument", () => { 13 | type res1 = Call>, [true, false, true]>; 14 | // ^? 15 | type test1 = Expect>; 16 | }); 17 | 18 | it("can be called with 2 pre-filled arguments", () => { 19 | type res1 = Call>; 20 | // ^? 21 | type test1 = Expect>; 22 | 23 | type res2 = Call>; 24 | // ^? 25 | type test2 = Expect>; 26 | 27 | type res3 = Call>; 28 | // ^? 29 | type test3 = Expect>; 30 | 31 | type res4 = Call>; 32 | // ^? 33 | type test4 = Expect>; 34 | }); 35 | }); 36 | 37 | describe("Or", () => { 38 | it("can be called without any pre-filled arguments", () => { 39 | type res1 = Call, [false, true, false]>; 40 | // ^? 41 | type test1 = Expect>; 42 | }); 43 | 44 | it("can be called with one pre-filled argument", () => { 45 | type res1 = Call>, [true, false, true]>; 46 | // ^? 47 | type test1 = Expect>; 48 | }); 49 | 50 | it("can be called with 2 pre-filled arguments", () => { 51 | type res1 = Call>; 52 | // ^? 53 | type test1 = Expect>; 54 | 55 | type res2 = Call>; 56 | // ^? 57 | type test2 = Expect>; 58 | 59 | type res3 = Call>; 60 | // ^? 61 | type test3 = Expect>; 62 | 63 | type res4 = Call>; 64 | // ^? 65 | type test4 = Expect>; 66 | }); 67 | }); 68 | 69 | describe("XOr", () => { 70 | it("can be called without any pre-filled arguments", () => { 71 | type res1 = Call, [false, true, false]>; 72 | // ^? 73 | type test1 = Expect>; 74 | }); 75 | 76 | it("can be called with one pre-filled argument", () => { 77 | type res1 = Call>, [true, false, true]>; 78 | // ^? 79 | type test1 = Expect>; 80 | }); 81 | 82 | it("can be called with 2 pre-filled arguments", () => { 83 | type res1 = Call>; 84 | // ^? 85 | type test1 = Expect>; 86 | 87 | type res2 = Call>; 88 | // ^? 89 | type test2 = Expect>; 90 | 91 | type res3 = Call>; 92 | // ^? 93 | type test3 = Expect>; 94 | 95 | type res4 = Call>; 96 | // ^? 97 | type test4 = Expect>; 98 | }); 99 | }); 100 | 101 | it("Not", () => { 102 | type res1 = Call; 103 | // ^? 104 | type test1 = Expect>; 105 | 106 | type res2 = Call>; 107 | // ^? 108 | type test2 = Expect>; 109 | }); 110 | 111 | describe("Extends", () => { 112 | it("should check if a type is assignable to another type", () => { 113 | type res1 = Call, [1, 2, 3]>; 114 | // ^? 115 | type test1 = Expect>; 116 | 117 | type res2 = Call>; 118 | // ^? 119 | type test2 = Expect>; 120 | 121 | type res3 = Call>; 122 | // ^? 123 | type test3 = Expect>; 124 | }); 125 | 126 | it("should reverse it's function arguments when partial applied", () => { 127 | type res4 = Call>; 128 | type test4 = Expect>; 129 | 130 | type res5 = Call, 1>; 131 | type test5 = Expect>; 132 | 133 | type res6 = Call>, [1, "2", 3]>; 134 | type test6 = Expect>; 135 | 136 | type res7 = Call, 1>; 137 | type test7 = Expect>; 138 | 139 | type res8 = Call>, [1, "2", 3]>; 140 | type test8 = Expect>; 141 | }); 142 | }); 143 | 144 | it("Equals", () => { 145 | type res1 = Call, ".">; 146 | // ^? 147 | type test1 = Expect>; 148 | }); 149 | 150 | it("DoesNotExtend", () => { 151 | type res1 = Call, "b">; 152 | // ^? 153 | type test1 = Expect>; 154 | }); 155 | }); 156 | -------------------------------------------------------------------------------- /test/core.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Call, 3 | Identity, 4 | Pipe, 5 | PipeRight, 6 | unset, 7 | _, 8 | } from "../src/internals/core/Core"; 9 | import { MergeArgs } from "../src/internals/core/impl/MergeArgs"; 10 | import { Equal, Expect } from "../src/internals/helpers"; 11 | import { Numbers } from "../src/internals/numbers/Numbers"; 12 | import { Strings } from "../src/internals/strings/Strings"; 13 | import { Tuples } from "../src/internals/tuples/Tuples"; 14 | 15 | describe("Core", () => { 16 | it("Identity", () => { 17 | // check primitives 18 | type res1 = Call; 19 | // ^? 20 | type tes1 = Expect>; 21 | type res2 = Call; 22 | // ^? 23 | type tes2 = Expect>; 24 | // check unions 25 | type res3 = Call; 26 | // ^? 27 | type tes3 = Expect>; 28 | }); 29 | 30 | describe("MergeArgs", () => { 31 | it("should remove unset args from partialArgs", () => { 32 | type pipedArgs1 = ["hello"]; 33 | type partialArgs1 = [unset, unset]; 34 | type res1 = MergeArgs; 35 | type test1 = Expect>; 36 | 37 | type pipedArgs2 = [1, 2]; 38 | type partialArgs2 = [unset, unset]; 39 | type res2 = MergeArgs; 40 | type test2 = Expect>; 41 | }); 42 | 43 | it("should support never", () => { 44 | type pipedArgs1 = ["hello"]; 45 | type partialArgs1 = ["a" | "b", never]; 46 | type res1 = MergeArgs; 47 | type test1 = Expect>; 48 | }); 49 | }); 50 | 51 | describe("Composition", () => { 52 | it("Pipe", () => { 53 | type res1 = Pipe< 54 | // ^? 55 | [1, 2, 3, 4, 3, 4, 124678765435897587654478964568576n], 56 | [ 57 | Tuples.Map>, 58 | Tuples.Join<".">, 59 | Strings.Split<".">, 60 | Tuples.Map, 61 | Tuples.Map>, 62 | Tuples.Map>, 63 | Tuples.Sum 64 | ] 65 | >; 66 | type tes1 = Expect>; 67 | }); 68 | 69 | it("PipeRight", () => { 70 | type res1 = PipeRight< 71 | // ^? 72 | [ 73 | Tuples.Sum, 74 | Tuples.Map>, 75 | Tuples.Map, 76 | Strings.Split<".">, 77 | Tuples.Join<".">, 78 | Tuples.Map> 79 | ], 80 | [1, 2, 3, 4, 3, 4] 81 | >; 82 | 83 | type tes1 = Expect>; 84 | }); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /test/functions.test.ts: -------------------------------------------------------------------------------- 1 | import { F, Numbers, Strings, Tuples, _ } from "../src/index"; 2 | import { Call } from "../src/internals/core/Core"; 3 | import { Equal, Expect } from "../src/internals/helpers"; 4 | 5 | describe("Functions", () => { 6 | it("Parameters", () => { 7 | type res1 = Call void>; 8 | // ^? 9 | type tes1 = Expect>; 10 | }); 11 | 12 | it("Parameter", () => { 13 | type res1 = Call, (a: string, b: number) => void>; 14 | // ^? 15 | type tes1 = Expect>; 16 | type res2 = Call void, 0>; 17 | // ^? 18 | type tes2 = Expect>; 19 | }); 20 | 21 | it("ReturnType", () => { 22 | type res1 = Call boolean>; 23 | // ^? 24 | type tes1 = Expect>; 25 | }); 26 | it("MapReturnType", () => { 27 | type res1 = Call< 28 | // ^? 29 | F.MapReturnType, 30 | (a: string, b: number) => "1" | "2" 31 | >; 32 | type tes1 = Expect 1 | 2>>; 33 | }); 34 | it("MapParameters", () => { 35 | type res1 = Call< 36 | // ^? 37 | F.MapParameters>, 38 | (a: "1" | "2", b: "3" | "4") => void 39 | >; 40 | type tes1 = Expect void>>; 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/match.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Booleans, 3 | Call, 4 | Match, 5 | Numbers, 6 | Objects, 7 | Strings, 8 | Tuples, 9 | Unions, 10 | } from "../src/index"; 11 | import { 12 | arg0, 13 | arg1, 14 | ComposeLeft, 15 | Constant, 16 | Pipe, 17 | } from "../src/internals/core/Core"; 18 | import { Equal, Expect } from "../src/internals/helpers"; 19 | 20 | describe("Match", () => { 21 | it("should match with regular types", () => { 22 | type MatchTest = Call< 23 | Match< 24 | T, 25 | [ 26 | Match.With<{ msg: string }, Constant<"a">>, 27 | Match.With>, 28 | Match.With> 29 | ] 30 | > 31 | >; 32 | 33 | type res1 = MatchTest<{ msg: "hello" }>; 34 | // ^? 35 | 36 | type test1 = Expect>; 37 | type res2 = MatchTest<"hello">; 38 | // ^? 39 | 40 | type test2 = Expect>; 41 | type res3 = MatchTest<1>; 42 | // ^? 43 | 44 | type test3 = Expect>; 45 | }); 46 | 47 | it("should work with patterns destructuring arguments", () => { 48 | type MatchTest = Call< 49 | Match< 50 | T, 51 | [ 52 | Match.With< 53 | { nested: { value: arg0 } }, 54 | Strings.Prepend<"nested.value === "> 55 | >, 56 | Match.With<{ x: arg0; y: arg1 }, Numbers.Add>, 57 | Match.With< 58 | { x: { y: [1, 2, arg0] } }, 59 | Strings.Prepend<"x.y[2] === "> 60 | >, 61 | Match.With>, 62 | Match.With> 63 | ] 64 | > 65 | >; 66 | 67 | type res1 = MatchTest<{ nested: { value: 123 } }>; 68 | // ^? 69 | type test1 = Expect>; 70 | 71 | type res2 = MatchTest<"world">; 72 | // ^? 73 | type test2 = Expect>; 74 | 75 | type res3 = MatchTest<1>; 76 | // ^? 77 | type test3 = Expect>; 78 | 79 | type res4 = MatchTest<{ x: 1; y: 2 }>; 80 | // ^? 81 | type test4 = Expect>; 82 | 83 | type res5 = MatchTest<{ x: { y: [1, 2, 3] } }>; 84 | // ^? 85 | type test5 = Expect>; 86 | }); 87 | 88 | it("should work with constrained arguments", () => { 89 | type MatchTest = Call< 90 | Match< 91 | T, 92 | [ 93 | Match.With<{ msg: arg0 }, Strings.Prepend<"msg: ">>, 94 | Match.With<{ x: arg0; y: arg1 }, Numbers.Add>, 95 | Match.With<{ x: arg0; y: arg1 }, Strings.Prepend> 96 | ] 97 | > 98 | >; 99 | 100 | type res1 = MatchTest<{ msg: "hello" }>; 101 | // ^? 102 | type test1 = Expect>; 103 | 104 | type res2 = MatchTest<{ x: 1; y: 2 }>; 105 | // ^? 106 | type test2 = Expect>; 107 | 108 | type res3 = MatchTest<{ x: "a"; y: "b" }>; 109 | // ^? 110 | type test3 = Expect>; 111 | }); 112 | 113 | it("Handlers can also be regular values", () => { 114 | type MatchTest = Call< 115 | Match< 116 | T, 117 | [ 118 | Match.With<{ msg: string }, "a">, 119 | Match.With, 120 | Match.With 121 | ] 122 | > 123 | >; 124 | 125 | type res1 = MatchTest<{ msg: "hello" }>; 126 | // ^? 127 | 128 | type test1 = Expect>; 129 | type res2 = MatchTest<"hello">; 130 | // ^? 131 | 132 | type test2 = Expect>; 133 | type res3 = MatchTest<1>; 134 | // ^? 135 | 136 | type test3 = Expect>; 137 | }); 138 | 139 | describe("Composition", () => { 140 | it("Map and Match", () => { 141 | type Transform = Call< 142 | Tuples.Map< 143 | Match< 144 | [ 145 | Match.With>, 146 | Match.With>, 147 | Match.With 148 | ] 149 | >, 150 | xs 151 | > 152 | >; 153 | 154 | type res1 = Transform<[1, 2, "101", true]>; 155 | // ^? 156 | type test1 = Expect>; 157 | }); 158 | 159 | it("RouteToParams", () => { 160 | type RouteToParams = Pipe< 161 | T, 162 | [ 163 | Strings.Split<"/">, 164 | Tuples.Filter>, 165 | Tuples.ToUnion, 166 | Unions.Map< 167 | ComposeLeft< 168 | [ 169 | Strings.Trim<"<" | ">">, 170 | Strings.Split<":">, 171 | Objects.Update< 172 | "[1]", 173 | Match< 174 | [ 175 | Match.With<"string", string>, 176 | Match.With<"number", number>, 177 | Match.With<"boolean", boolean> 178 | ] 179 | > 180 | > 181 | ] 182 | > 183 | >, 184 | Objects.FromEntries 185 | ] 186 | >; 187 | 188 | type res1 = RouteToParams<"/users//posts/">; 189 | // ^? 190 | type test1 = Expect>; 191 | 192 | type res2 = RouteToParams<"/dashboard/">; 193 | // ^? 194 | type test2 = Expect>; 195 | }); 196 | }); 197 | }); 198 | -------------------------------------------------------------------------------- /test/numbers.test.ts: -------------------------------------------------------------------------------- 1 | import { Call, Numbers, Tuples, _, T } from "../src/index"; 2 | import { Equal, Expect } from "../src/internals/helpers"; 3 | 4 | describe("Numbers", () => { 5 | describe("Add", () => { 6 | it("can be called without any pre-filled arguments", () => { 7 | type res1 = Call, [1, 2, 3]>; 8 | // ^? 9 | 10 | type test1 = Expect>; 11 | }); 12 | 13 | it("can be called with one pre-filled argument", () => { 14 | type res1 = Call>, [1, 2, 3]>; 15 | // ^? 16 | 17 | type test1 = Expect>; 18 | }); 19 | 20 | it("can be called with 2 pre-filled arguments", () => { 21 | type res1 = Call>; 22 | // ^? 23 | 24 | type test1 = Expect>; 25 | }); 26 | }); 27 | 28 | describe("Sub", () => { 29 | it("can be called without any pre-filled arguments", () => { 30 | type res1 = Call, [1, 2, 3]>; 31 | // ^? 32 | type test1 = Expect>; 33 | 34 | type res2 = Call; 35 | // ^? 36 | type test2 = Expect>; 37 | }); 38 | 39 | it("can be called with one pre-filled argument", () => { 40 | type res1 = Call>, [1, 2, 3]>; 41 | // ^? 42 | type test1 = Expect>; 43 | }); 44 | 45 | it("can be called with 2 pre-filled arguments", () => { 46 | type res1 = Call>; 47 | // ^? 48 | type test1 = Expect>; 49 | }); 50 | it("should reverse it's function arguments when partial applied", () => { 51 | type res1 = Call, 2>; 52 | // ^? 53 | type test1 = Expect>; 54 | }); 55 | 56 | it("shouldn't reverse if the position is explicit", () => { 57 | type res1 = Call, 2>; 58 | // ^? 59 | type test1 = Expect>; 60 | }); 61 | }); 62 | 63 | describe("Mul", () => { 64 | it("can be called without any pre-filled arguments", () => { 65 | type res1 = Call, [1, 2, 3]>; 66 | // ^? 67 | type test1 = Expect>; 68 | 69 | type res2 = Call; 70 | // ^? 71 | type test2 = Expect>; 72 | 73 | type res3 = Call; 74 | // ^? 75 | type test3 = Expect>; 76 | }); 77 | 78 | it("can be called with one pre-filled argument", () => { 79 | type res1 = Call>, [1, 2, 3]>; 80 | // ^? 81 | type test1 = Expect>; 82 | }); 83 | 84 | it("can be called with 2 pre-filled arguments", () => { 85 | type res1 = Call>; 86 | // ^? 87 | type test1 = Expect>; 88 | }); 89 | }); 90 | 91 | describe("Div", () => { 92 | it("can be called without any pre-filled arguments", () => { 93 | type res1 = Call, [1, 2, 3]>; 94 | // ^? 95 | type test1 = Expect>; 96 | 97 | type res2 = Call; 98 | // ^? 99 | type test2 = Expect>; 100 | }); 101 | 102 | it("can be called with one pre-filled argument", () => { 103 | type res1 = Call>, [2, 4, 6]>; 104 | // ^? 105 | type test1 = Expect>; 106 | }); 107 | 108 | it("can be called with 2 pre-filled arguments", () => { 109 | type res1 = Call>; 110 | // ^? 111 | type test1 = Expect>; 112 | }); 113 | }); 114 | 115 | describe("Mod", () => { 116 | it("can be called without any pre-filled arguments", () => { 117 | type res2 = Call; 118 | // ^? 119 | type test2 = Expect>; 120 | }); 121 | 122 | it("can be called with one pre-filled argument", () => { 123 | type res1 = Call>, [2, 4, 6]>; 124 | // ^? 125 | type test1 = Expect>; 126 | }); 127 | 128 | it("can be called with 2 pre-filled arguments", () => { 129 | type res1 = Call>; 130 | // ^? 131 | type test1 = Expect>; 132 | }); 133 | }); 134 | 135 | describe("Negate", () => { 136 | it("can be called without any pre-filled arguments", () => { 137 | type res1 = Call, [1, 2, 3]>; 138 | // ^? 139 | type test1 = Expect>; 140 | }); 141 | 142 | it("can be called with 1 pre-filled arguments", () => { 143 | type res1 = Call>; 144 | // ^? 145 | type test1 = Expect>; 146 | }); 147 | }); 148 | 149 | describe("Abs", () => { 150 | it("can be called without any pre-filled arguments", () => { 151 | type res1 = Call, [-1, 2, -3]>; 152 | // ^? 153 | type test1 = Expect>; 154 | }); 155 | 156 | it("can be called with 1 pre-filled arguments", () => { 157 | type res1 = Call>; 158 | // ^? 159 | type test1 = Expect>; 160 | }); 161 | }); 162 | 163 | describe("Power", () => { 164 | it("can be called without any pre-filled arguments", () => { 165 | type res1 = Call, [1, 2, 3]>; 166 | // ^? 167 | type test1 = Expect>; 168 | 169 | type res2 = Call; 170 | // ^? 171 | type test2 = Expect>; 172 | 173 | type res3 = Call; 174 | // ^? 175 | type test3 = Expect>; 176 | }); 177 | 178 | it("can be called with one pre-filled arguments", () => { 179 | type res1 = Call>, [1, 2, 3]>; 180 | // ^? 181 | type test1 = Expect>; 182 | }); 183 | 184 | it("can be called with 1 pre-filled arguments", () => { 185 | type res1 = Call>; 186 | // ^? 187 | type test1 = Expect>; 188 | }); 189 | }); 190 | 191 | describe("Min", () => { 192 | it("can be called without any pre-filled arguments", () => { 193 | type res1 = Call, [1, 2, 3]>; 194 | // ^? 195 | type test1 = Expect>; 196 | 197 | type res2 = Call; 198 | // ^? 199 | type test2 = Expect>; 200 | 201 | type res3 = Call; 202 | // ^? 203 | type test3 = Expect>; 204 | }); 205 | 206 | it("can be called with one pre-filled arguments", () => { 207 | type res1 = Call>, [1, 2, 3]>; 208 | // ^? 209 | type test1 = Expect>; 210 | }); 211 | 212 | it("can be called with 1 pre-filled arguments", () => { 213 | type res1 = Call>; 214 | // ^? 215 | type test1 = Expect>; 216 | }); 217 | }); 218 | 219 | describe("Max", () => { 220 | it("can be called without any pre-filled arguments", () => { 221 | type res1 = Call, [1, 2, 3]>; 222 | // ^? 223 | type test1 = Expect>; 224 | 225 | type res2 = Call; 226 | // ^? 227 | type test2 = Expect>; 228 | 229 | type res3 = Call; 230 | // ^? 231 | type test3 = Expect>; 232 | }); 233 | 234 | it("can be called with one pre-filled arguments", () => { 235 | type res1 = Call>, [1, 2, 3]>; 236 | // ^? 237 | type test1 = Expect>; 238 | }); 239 | 240 | it("can be called with 1 pre-filled arguments", () => { 241 | type res1 = Call>; 242 | // ^? 243 | type test1 = Expect>; 244 | }); 245 | }); 246 | 247 | describe("Compare", () => { 248 | it("can be called without any pre-filled arguments", () => { 249 | type res1 = Call; 250 | // ^? 251 | type test1 = Expect>; 252 | 253 | type res2 = Call; 254 | // ^? 255 | type test2 = Expect>; 256 | 257 | type res3 = Call; 258 | // ^? 259 | type test3 = Expect>; 260 | }); 261 | 262 | it("can be called with one pre-filled arguments", () => { 263 | type res1 = Call>, [1, 2, 3]>; 264 | // ^? 265 | type test1 = Expect>; 266 | }); 267 | 268 | it("can be called with 1 pre-filled arguments", () => { 269 | type res1 = Call>; 270 | // ^? 271 | type test1 = Expect>; 272 | }); 273 | }); 274 | 275 | describe("LessThan", () => { 276 | it("can be called without any pre-filled arguments", () => { 277 | type res1 = Call; 278 | // ^? 279 | type test1 = Expect>; 280 | 281 | type res2 = Call; 282 | // ^? 283 | type test2 = Expect>; 284 | 285 | type res3 = Call; 286 | // ^? 287 | type test3 = Expect>; 288 | }); 289 | 290 | it("can be called with one pre-filled arguments", () => { 291 | type res1 = Call>, [1, 2, 3]>; 292 | // ^? 293 | type test1 = Expect>; 294 | }); 295 | 296 | it("can be called with 1 pre-filled arguments", () => { 297 | type res1 = Call>; 298 | // ^? 299 | type test1 = Expect>; 300 | }); 301 | }); 302 | 303 | describe("LessThanOrEqual", () => { 304 | it("can be called without any pre-filled arguments", () => { 305 | type res1 = Call; 306 | // ^? 307 | type test1 = Expect>; 308 | 309 | type res2 = Call; 310 | // ^? 311 | type test2 = Expect>; 312 | 313 | type res3 = Call; 314 | // ^? 315 | type test3 = Expect>; 316 | }); 317 | 318 | it("can be called with one pre-filled arguments", () => { 319 | type res1 = Call>, [1, 2, 3]>; 320 | // ^? 321 | type test1 = Expect>; 322 | }); 323 | 324 | it("can be called with 1 pre-filled arguments", () => { 325 | type res1 = Call>; 326 | // ^? 327 | type test1 = Expect>; 328 | }); 329 | }); 330 | 331 | describe("GreaterThan", () => { 332 | it("can be called without any pre-filled arguments", () => { 333 | type res1 = Call; 334 | // ^? 335 | type test1 = Expect>; 336 | 337 | type res2 = Call; 338 | // ^? 339 | type test2 = Expect>; 340 | 341 | type res3 = Call; 342 | // ^? 343 | type test3 = Expect>; 344 | }); 345 | 346 | it("can be called with one pre-filled arguments", () => { 347 | type res1 = Call>, [1, 2, 3]>; 348 | // ^? 349 | type test1 = Expect>; 350 | }); 351 | 352 | it("can be called with 1 pre-filled arguments", () => { 353 | type res1 = Call>; 354 | // ^? 355 | type test1 = Expect>; 356 | }); 357 | 358 | it("should reverse it's function arguments when partial applied", () => { 359 | type res4 = Call>; 360 | type test4 = Expect>; 361 | 362 | type res5 = Call, 1>; 363 | type test5 = Expect>; 364 | 365 | type res6 = Call>, [1, 2, 3]>; 366 | type test6 = Expect>; 367 | 368 | type res7 = Call, 1>; 369 | type test7 = Expect>; 370 | 371 | type res8 = Call>, [1, 2, 3]>; 372 | type test8 = Expect>; 373 | }); 374 | }); 375 | 376 | describe("GreaterThanOrEqual", () => { 377 | it("can be called without any pre-filled arguments", () => { 378 | type res1 = Call; 379 | // ^? 380 | type test1 = Expect>; 381 | 382 | type res2 = Call; 383 | // ^? 384 | type test2 = Expect>; 385 | 386 | type res3 = Call; 387 | // ^? 388 | type test3 = Expect>; 389 | }); 390 | 391 | it("can be called with one pre-filled arguments", () => { 392 | type res1 = Call>, [1, 2, 3]>; 393 | // ^? 394 | type test1 = Expect>; 395 | }); 396 | 397 | it("can be called with 1 pre-filled arguments", () => { 398 | type res1 = Call>; 399 | // ^? 400 | type test1 = Expect>; 401 | }); 402 | }); 403 | }); 404 | -------------------------------------------------------------------------------- /test/real-world/reselect.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use case from @markerikson 3 | * https://github.com/gvergnaud/hotscript/discussions/65#discussioncomment-5109155 4 | */ 5 | 6 | import * as H from "../../src/index"; 7 | import { Equal, Expect } from "../../src/internals/helpers"; 8 | 9 | describe("Reselect", () => { 10 | type MergeParameters = H.Pipe< 11 | T, 12 | [ 13 | H.Tuples.Map, 14 | PadWithUnknown, 15 | ApplyArg, 16 | H.Tuples.Map 17 | ] 18 | >; 19 | 20 | interface ApplyArg extends H.Fn { 21 | return: H.Apply; 22 | } 23 | 24 | /** 25 | * Make sure all lists of arguments have the same length by 26 | * adding unknown arguments at the end of the shorter ones. 27 | * @example 28 | * ```ts 29 | * type T = Call> 30 | * // [[unknown, unknown], [1, unknown], [1, 2]] 31 | * ``` 32 | */ 33 | interface PadWithUnknown extends H.Fn { 34 | return: this["args"] extends [infer argsList extends any[][]] 35 | ? H.Pipe< 36 | argsList, 37 | [ 38 | GetMaxLength, 39 | GetUnknownPadding, 40 | H.Tuples.ZipWith 41 | ] 42 | > 43 | : never; 44 | } 45 | 46 | type GetMaxLength = H.ComposeLeft< 47 | [H.Tuples.Map, H.Tuples.Reduce] 48 | >; 49 | 50 | /** 51 | * Returns a list of `unknown` to pad the argsList 52 | * to make them all have the same length. 53 | * @example 54 | * ```ts 55 | * type T = Call, 2> 56 | * // [[unknown, unknown], [unknown], []] 57 | * ``` 58 | */ 59 | interface GetUnknownPadding extends H.Fn { 60 | return: H.Call< 61 | H.Tuples.Map< 62 | H.ComposeLeft< 63 | [ 64 | H.Tuples.Length, 65 | H.Numbers.Sub, 66 | H.Tuples.Range<0>, 67 | H.Tuples.Map>, 68 | H.Tuples.Tail 69 | ] 70 | >, 71 | argsList 72 | > 73 | >; 74 | } 75 | 76 | it("MergeParameters", () => { 77 | type res1 = MergeParameters<[(a: number) => boolean, (c: 42) => string]>; 78 | // ^? 79 | type test1 = Expect>; 80 | 81 | type res2 = MergeParameters< 82 | // ^? 83 | [ 84 | (a: number | string) => boolean, 85 | (a: number) => boolean, 86 | (c: 42) => string 87 | ] 88 | >; 89 | type test2 = Expect>; 90 | 91 | type res3 = MergeParameters< 92 | // ^? 93 | [ 94 | (a: number | string) => boolean, 95 | (a: string, b: string, c: string) => boolean, 96 | (c: "hello") => string 97 | ] 98 | >; 99 | type test3 = Expect>; 100 | 101 | type res4 = MergeParameters< 102 | // ^? 103 | [ 104 | (a: number | string) => boolean, 105 | (a: string, b: string) => boolean, 106 | (c: number, b: string) => string 107 | ] 108 | >; 109 | type test4 = Expect>; 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /test/strings.test.ts: -------------------------------------------------------------------------------- 1 | import { _, $, Strings } from "../src/index"; 2 | import { Compose } from "../src/internals/core/Core"; 3 | import { Equal, Expect } from "../src/internals/helpers"; 4 | 5 | describe("Strings", () => { 6 | it("Length", () => { 7 | type res1 = $; 8 | // ^? 9 | type test1 = Expect>; 10 | type res2 = $; 11 | // ^? 12 | type test2 = Expect>; 13 | type res3 = $< 14 | // ^? 15 | Compose<[Strings.Length, Strings.Repeat<1001>]>, 16 | "a" 17 | >; 18 | type test3 = Expect>; 19 | }); 20 | 21 | it("TrimLeft", () => { 22 | type res1 = $; 23 | // ^? 24 | type test1 = Expect>; 25 | type res2 = $, "0001000">; 26 | // ^? 27 | type test2 = Expect>; 28 | }); 29 | 30 | it("TrimRight", () => { 31 | type res1 = $; 32 | // ^? 33 | type test1 = Expect>; 34 | type res2 = $, "0001000">; 35 | // ^? 36 | type test2 = Expect>; 37 | }); 38 | 39 | it("Trim", () => { 40 | type res1 = $; 41 | // ^? 42 | type test1 = Expect>; 43 | type res2 = $, "0001000">; 44 | // ^? 45 | type test2 = Expect>; 46 | }); 47 | 48 | describe("Replace", () => { 49 | it("replaces single letters", () => { 50 | type res1 = $, "abc">; 51 | // ^? 52 | type test1 = Expect>; 53 | }); 54 | 55 | it("is identity on empty strings", () => { 56 | type res2 = $, "">; 57 | // ^? 58 | type test2 = Expect>; 59 | }); 60 | 61 | it("replacing by empty string", () => { 62 | type res3 = $, "abc">; 63 | // ^? 64 | type test3 = Expect>; 65 | }); 66 | 67 | it("supports multi char strings", () => { 68 | type res4 = $, "hello world!">; 69 | // ^? 70 | type test4 = Expect>; 71 | 72 | type res5 = $, "many more than many">; 73 | // ^? 74 | type test5 = Expect>; 75 | }); 76 | 77 | it("supports union types", () => { 78 | type res6 = $, "abc">; 79 | // ^? 80 | type test6 = Expect>; 81 | 82 | type res4 = $< 83 | // ^? 84 | Strings.Replace<"hello" | "hi", "sup">, 85 | "hello world! hi!" 86 | >; 87 | type test4 = Expect>; 88 | }); 89 | }); 90 | 91 | it("Slice", () => { 92 | type res1 = $, "123">; 93 | // ^? 94 | type test1 = Expect>; 95 | type res2 = $, "123">; 96 | // ^? 97 | type test2 = Expect>; 98 | type res3 = $, "123">; 99 | // ^? 100 | type test3 = Expect>; 101 | type res4 = $, "123">; 102 | // ^? 103 | type test4 = Expect>; 104 | }); 105 | 106 | it("Split", () => { 107 | type res1 = $, "1.2.3">; 108 | // ^? 109 | type test1 = Expect>; 110 | type res2 = $, "123">; 111 | // ^? 112 | type test2 = Expect>; 113 | type res3 = $, "">; 114 | // ^? 115 | type test3 = Expect>; 116 | type res4 = $, "1--2-3.4..5">; 117 | // ^? 118 | type test4 = Expect>; 119 | }); 120 | 121 | it("Repeat", () => { 122 | type res1 = $, "a">; 123 | // ^? 124 | type test1 = Expect>; 125 | type res2 = $, "a">; 126 | // ^? 127 | type test2 = Expect>; 128 | type res3 = $, "a">; 129 | // ^? 130 | type test3 = Expect>; 131 | type res4 = $, "hello!">; 132 | // ^? 133 | type test4 = Expect>; 134 | }); 135 | 136 | it("StartsWith", () => { 137 | type res1 = $, "hello world">; 138 | // ^? 139 | type test1 = Expect>; 140 | type res2 = $, "world hello">; 141 | // ^? 142 | type test2 = Expect>; 143 | type res3 = $, "hello world">; 144 | // ^? 145 | type test3 = Expect>; 146 | type res4 = $, "">; 147 | // ^? 148 | type test4 = Expect>; 149 | }); 150 | 151 | it("EndsWith", () => { 152 | type res1 = $, "hello world">; 153 | // ^? 154 | type test1 = Expect>; 155 | type res2 = $, "world hello">; 156 | // ^? 157 | type test2 = Expect>; 158 | type res3 = $, "hello world">; 159 | // ^? 160 | type test3 = Expect>; 161 | type res4 = $, "">; 162 | // ^? 163 | type test4 = Expect>; 164 | }); 165 | 166 | it("ToTuple", () => { 167 | type res1 = $; 168 | // ^? 169 | type test1 = Expect>; 170 | type res2 = $; 171 | // ^? 172 | type test2 = Expect>; 173 | }); 174 | 175 | it("ToNumber", () => { 176 | type res1 = $; 177 | // ^? 178 | type test1 = Expect>; 179 | }); 180 | 181 | it("ToString", () => { 182 | type res1 = $; 183 | // ^? 184 | type test1 = Expect>; 185 | }); 186 | 187 | it("Prepend", () => { 188 | type res1 = $, "abc">; 189 | // ^? 190 | type test1 = Expect>; 191 | }); 192 | 193 | it("Append", () => { 194 | type res1 = $, "abc">; 195 | // ^? 196 | type test1 = Expect>; 197 | }); 198 | 199 | it("Uppercase", () => { 200 | type res1 = $; 201 | // ^? 202 | type test1 = Expect>; 203 | }); 204 | 205 | it("Lowercase", () => { 206 | type res1 = $; 207 | // ^? 208 | type test1 = Expect>; 209 | }); 210 | 211 | it("Capitalize", () => { 212 | type res1 = $; 213 | // ^? 214 | type test1 = Expect>; 215 | }); 216 | 217 | it("Uncapitalize", () => { 218 | type res1 = $; 219 | // ^? 220 | type test1 = Expect>; 221 | }); 222 | 223 | it("SnakeCase", () => { 224 | type res1 = $; 225 | // ^? 226 | type test1 = Expect>; 227 | type res2 = $; 228 | // ^? 229 | type test2 = Expect>; 230 | }); 231 | 232 | it("KebabCase", () => { 233 | type res1 = $; 234 | // ^? 235 | type test1 = Expect>; 236 | type res2 = $; 237 | // ^? 238 | type test2 = Expect>; 239 | }); 240 | 241 | it("CamelCase", () => { 242 | type res1 = $; 243 | // ^? 244 | type test1 = Expect>; 245 | }); 246 | 247 | it("Compare", () => { 248 | type res1 = $, "a">; 249 | // ^? 250 | type test1 = Expect>; 251 | type res2 = $, "b">; 252 | // ^? 253 | type test2 = Expect>; 254 | type res3 = $, "b">; 255 | // ^? 256 | type test3 = Expect>; 257 | type res4 = $, "a">; 258 | // ^? 259 | type test4 = Expect>; 260 | type res5 = $; 261 | // ^? 262 | type test5 = Expect>; 263 | type res6 = $; 264 | // ^? 265 | type test6 = Expect>; 266 | type res7 = $; 267 | // ^? 268 | type test7 = Expect>; 269 | type res8 = $; 270 | // ^? 271 | type test8 = Expect>; 272 | }); 273 | 274 | it("LessThan", () => { 275 | type res1 = $, "b">; 276 | // ^? 277 | type test1 = Expect>; 278 | type res2 = $, "a">; 279 | // ^? 280 | type test2 = Expect>; 281 | type res3 = $, "a">; 282 | // ^? 283 | type test3 = Expect>; 284 | type res4 = $; 285 | // ^? 286 | type test4 = Expect>; 287 | }); 288 | 289 | it("LessThanOrEqual", () => { 290 | type res1 = $, "b">; 291 | // ^? 292 | type test1 = Expect>; 293 | type res2 = $, "a">; 294 | // ^? 295 | type test2 = Expect>; 296 | type res3 = $, "a">; 297 | // ^? 298 | type test3 = Expect>; 299 | type res4 = $; 300 | // ^? 301 | type test4 = Expect>; 302 | }); 303 | 304 | it("GreaterThan", () => { 305 | type res1 = $, "b">; 306 | // ^? 307 | type test1 = Expect>; 308 | type res2 = $, "a">; 309 | // ^? 310 | type test2 = Expect>; 311 | type res3 = $, "a">; 312 | // ^? 313 | type test3 = Expect>; 314 | type res4 = $; 315 | // ^? 316 | type test4 = Expect>; 317 | }); 318 | 319 | it("GreaterThanOrEqual", () => { 320 | type res1 = $, "b">; 321 | // ^? 322 | type test1 = Expect>; 323 | type res2 = $, "a">; 324 | // ^? 325 | type test2 = Expect>; 326 | type res3 = $, "a">; 327 | // ^? 328 | type test3 = Expect>; 329 | type res4 = $; 330 | // ^? 331 | type test4 = Expect>; 332 | }); 333 | }); 334 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "noEmit": true, 5 | "target": "ES2020", 6 | "moduleResolution": "node" 7 | }, 8 | "exclude": ["src/", "dist/", "example/"] 9 | } 10 | -------------------------------------------------------------------------------- /test/tuples.test.ts: -------------------------------------------------------------------------------- 1 | import { Booleans } from "../src/internals/booleans/Booleans"; 2 | import { Call, Fn, Pipe, _ } from "../src/internals/core/Core"; 3 | import { Equal, Expect } from "../src/internals/helpers"; 4 | import { Numbers } from "../src/internals/numbers/Numbers"; 5 | import { Strings } from "../src/internals/strings/Strings"; 6 | import { Tuples } from "../src/internals/tuples/Tuples"; 7 | 8 | describe("Tuples", () => { 9 | it("Head", () => { 10 | type res1 = Call; 11 | // ^? 12 | type tes1 = Expect>; 13 | 14 | type res2 = Call>; 15 | // ^? 16 | type tes2 = Expect>; 17 | }); 18 | 19 | it("Tail", () => { 20 | type res1 = Call; 21 | // ^? 22 | type tes1 = Expect>; 23 | 24 | type res2 = Call>; 25 | // ^? 26 | type tes2 = Expect>; 27 | }); 28 | 29 | it("Last", () => { 30 | type res1 = Call; 31 | // ^? 32 | type tes1 = Expect>; 33 | 34 | type res2 = Call>; 35 | // ^? 36 | type tes2 = Expect>; 37 | }); 38 | 39 | it("Map", () => { 40 | interface ToPhrase extends Fn { 41 | return: `number is ${Extract}`; 42 | } 43 | 44 | type res1 = Call, [1, 2, 3]>; 45 | // ^? 46 | type tes1 = Expect< 47 | Equal 48 | >; 49 | 50 | type res2 = Call>; 51 | // ^? 52 | type tes2 = Expect< 53 | Equal 54 | >; 55 | }); 56 | 57 | it("Filter", () => { 58 | interface IsNumber extends Fn { 59 | return: this["arg0"] extends number ? true : false; 60 | } 61 | 62 | type res1 = Call, [1, 2, "oops", 3]>; 63 | // ^? 64 | type tes1 = Expect>; 65 | 66 | type res2 = Call, readonly [1, 2, "oops", 3]>; 67 | // ^? 68 | type tes2 = Expect>; 69 | 70 | type res3 = Call>; 71 | // ^? 72 | type tes3 = Expect>; 73 | 74 | type res4 = Call>; 75 | // ^? 76 | type tes4 = Expect>; 77 | }); 78 | 79 | it("Reduce", () => { 80 | interface ToUnaryTupleArray extends Fn { 81 | return: this["args"] extends [infer acc extends any[], infer item] 82 | ? [...acc, [item]] 83 | : never; 84 | } 85 | 86 | type res1 = Call, [1, 2, 3]>; 87 | // ^? 88 | type tes1 = Expect>; 89 | 90 | type res2 = Call>; 91 | // ^? 92 | type tes2 = Expect>; 93 | }); 94 | 95 | it("ReduceRight", () => { 96 | interface ToUnaryTupleArray extends Fn { 97 | return: this["args"] extends [infer acc extends any[], infer item] 98 | ? [...acc, [item]] 99 | : never; 100 | } 101 | 102 | type res1 = Call< 103 | // ^? 104 | Tuples.ReduceRight, 105 | [1, 2, 3] 106 | >; 107 | type tes1 = Expect>; 108 | 109 | type res2 = Call>; 110 | // ^? 111 | type tes2 = Expect>; 112 | }); 113 | 114 | it("FlatMap", () => { 115 | interface Duplicate extends Fn { 116 | return: [this["arg0"], this["arg0"]]; 117 | } 118 | 119 | type res1 = Call, [1, 2, 3]>; 120 | // ^? 121 | type tes1 = Expect>; 122 | 123 | type res2 = Call>; 124 | // ^? 125 | type tes2 = Expect>; 126 | }); 127 | 128 | it("Find", () => { 129 | interface IsNumber extends Fn { 130 | return: this["arg0"] extends number ? true : false; 131 | } 132 | 133 | type res1 = Call, ["a", "b", "c", 2, "d"]>; 134 | // ^? 135 | type tes1 = Expect>; 136 | 137 | interface IsSecond extends Fn { 138 | return: this["arg1"] extends 1 ? true : false; 139 | } 140 | 141 | type res2 = Call, ["a", "b", "c", 2, "d"]>; 142 | // ^? 143 | type tes2 = Expect>; 144 | 145 | type res3 = Call>; 146 | // ^? 147 | type tes3 = Expect>; 148 | }); 149 | 150 | it("Reverse", () => { 151 | type res1 = Call; 152 | // ^? 153 | type tes1 = Expect>; 154 | }); 155 | 156 | it("Drop", () => { 157 | type res1 = Call, ["a", "b", "c", 2, "d"]>; 158 | // ^? 159 | type tes1 = Expect>; 160 | 161 | type res2 = Call, ["a", "b", "c", 2, "d"]>; 162 | // ^? 163 | type tes2 = Expect>; 164 | 165 | type res3 = Call>; 166 | // ^? 167 | type tes3 = Expect>; 168 | }); 169 | 170 | it("Take", () => { 171 | type res1 = Call, ["a", "b", "c", 2, "d"]>; 172 | // ^? 173 | type tes1 = Expect>; 174 | 175 | type res2 = Call, ["a", "b", "c", 2, "d"]>; 176 | // ^? 177 | type tes2 = Expect>; 178 | 179 | type res3 = Call>; 180 | // ^? 181 | type tes3 = Expect>; 182 | }); 183 | 184 | it("TakeWhile", () => { 185 | type res1 = Call< 186 | // ^? 187 | Tuples.TakeWhile>, 188 | ["a", "b", "c", 2, "d"] 189 | >; 190 | type tes1 = Expect>; 191 | 192 | type res2 = Call< 193 | // ^? 194 | Tuples.TakeWhile>, 195 | [1, 2, "a", "b", "c", 2, "d"] 196 | >; 197 | type tes2 = Expect>; 198 | 199 | type res3 = Call< 200 | // ^? 201 | Tuples.TakeWhile< 202 | Booleans.Extends<_, number>, 203 | [1, 2, "a", "b", "c", 2, "d"] 204 | > 205 | >; 206 | type tes3 = Expect>; 207 | }); 208 | 209 | it("Every", () => { 210 | type res1 = Call< 211 | // ^? 212 | Tuples.Every>, 213 | ["a", "b", "c", "d"] 214 | >; 215 | type tes1 = Expect>; 216 | 217 | type res2 = Call< 218 | // ^? 219 | Tuples.Every>, 220 | [1, 2, "a", "b", "c", 2, "d"] 221 | >; 222 | type tes2 = Expect>; 223 | }); 224 | 225 | it("Some", () => { 226 | type res1 = Call< 227 | // ^? 228 | Tuples.Some>, 229 | ["a", "b", "c", "d"] 230 | >; 231 | type tes1 = Expect>; 232 | 233 | type res2 = Call< 234 | // ^? 235 | Tuples.Some>, 236 | [1, 2, "a", "b", "c", 2, "d"] 237 | >; 238 | type tes2 = Expect>; 239 | }); 240 | 241 | it("Sort Numbers (default)", () => { 242 | type res1 = Call< 243 | // ^? 244 | Tuples.Sort, 245 | [7, 1, 3, 2, 6, 5, 8, 4] 246 | >; 247 | type tes1 = Expect>; 248 | }); 249 | 250 | it("Sort Numbers (custom)", () => { 251 | type res1 = Call< 252 | // ^? 253 | Tuples.Sort, 254 | [7, 1, 3, 2, 6, 5, 8, 4] 255 | >; 256 | type tes1 = Expect>; 257 | }); 258 | 259 | it("Sort Strings (custom)", () => { 260 | type res1 = Call< 261 | // ^? 262 | Tuples.Sort, 263 | ["c", "a", "f", "b", "e", "d"] 264 | >; 265 | type tes1 = Expect>; 266 | }); 267 | 268 | it("Join", () => { 269 | type res1 = Call, [1, 2, 3]>; 270 | // ^? 271 | type test1 = Expect>; 272 | }); 273 | 274 | it("Append", () => { 275 | type res1 = Call, [1, 2, 3]>; 276 | // ^? 277 | type test1 = Expect>; 278 | 279 | type res2 = Call>; 280 | // ^? 281 | type test2 = Expect>; 282 | }); 283 | 284 | it("Prepend", () => { 285 | type res1 = Call, [1, 2, 3]>; 286 | // ^? 287 | type test1 = Expect>; 288 | 289 | type res2 = Call>; 290 | // ^? 291 | type test2 = Expect>; 292 | }); 293 | 294 | it("Concat", () => { 295 | type res1 = Call, [1, 2, 3]>; 296 | // ^? 297 | type test1 = Expect>; 298 | 299 | type res2 = Call>; 300 | // ^? 301 | type test2 = Expect>; 302 | }); 303 | 304 | it("Partition", () => { 305 | type res1 = Call< 306 | // ^? 307 | Tuples.Partition>, 308 | [1, "a", 2, "b", 3, "c"] 309 | >; 310 | type test1 = Expect>; 311 | 312 | type res2 = Pipe< 313 | // ^? 314 | [1, "a", 2, "b", 3, "c"], 315 | [Tuples.Partition>, Tuples.At<0>, Tuples.At<2>] 316 | >; 317 | type test2 = Expect>; 318 | }); 319 | 320 | it("At", () => { 321 | type res1 = Call< 322 | // ^? 323 | Tuples.At<2, [1, "a", 2, "b", 3, "c"]> 324 | >; 325 | type test1 = Expect>; 326 | 327 | // check out of bounds 328 | type res2 = Call< 329 | // ^? 330 | Tuples.At<6, [1, "a", 2, "b", 3, "c"]> 331 | >; 332 | type test2 = Expect>; 333 | }); 334 | 335 | it("SplitAt", () => { 336 | type res0 = Call, [1, 2, 3, 4]>; 337 | // ^? 338 | type test0 = Expect>; 339 | 340 | type res1 = Call, [1]>; 341 | // ^? 342 | type test1 = Expect>; 343 | }); 344 | 345 | it("IsEmpty", () => { 346 | type res1 = Call< 347 | // ^? 348 | Tuples.IsEmpty<[1, "a", 2, "b", 3, "c"]> 349 | >; 350 | type test1 = Expect>; 351 | 352 | type res2 = Call< 353 | // ^? 354 | Tuples.IsEmpty, 355 | [] 356 | >; 357 | type test2 = Expect>; 358 | 359 | type res3 = Call< 360 | // ^? 361 | Tuples.IsEmpty<[]> 362 | >; 363 | type test3 = Expect>; 364 | }); 365 | 366 | it("Zip", () => { 367 | type x = Call>; 368 | // ^? 369 | 370 | type res1 = Call< 371 | // ^? 372 | Tuples.Zip<[1, 2, 3]>, 373 | ["a", "b", "c"] 374 | >; 375 | type test1 = Expect>; 376 | 377 | type res2 = Call< 378 | // ^? 379 | Tuples.Zip, 380 | [1, 2, 3], 381 | ["a", "b", "c"] 382 | >; 383 | type test2 = Expect>; 384 | 385 | type res3 = Call< 386 | // ^? 387 | Tuples.Zip<[1, 2, 3], ["a", "b", "c"]> 388 | >; 389 | type test3 = Expect>; 390 | 391 | type res4 = Call< 392 | // ^? 393 | Tuples.Zip<[1, 2, 3], ["a", "b", "c"], [true, false, true]> 394 | >; 395 | type test4 = Expect< 396 | Equal 397 | >; 398 | }); 399 | 400 | it("ZipWith", () => { 401 | type res1 = Call< 402 | // ^? 403 | Tuples.ZipWith, 404 | [1, 2, 3], 405 | [4, 5, 6] 406 | >; 407 | type test1 = Expect>; 408 | 409 | type res2 = Call< 410 | // ^? 411 | Tuples.ZipWith 412 | >; 413 | type test2 = Expect>; 414 | 415 | type res3 = Pipe< 416 | // ^? 417 | [1, 2, 3], 418 | [Tuples.ZipWith] 419 | >; 420 | type test3 = Expect>; 421 | }); 422 | 423 | describe("GroupBy", () => { 424 | interface GetTypeKey extends Fn { 425 | return: this["arg0"] extends { type: infer Type } ? Type : never; 426 | } 427 | type res1 = Call< 428 | // ^? 429 | Tuples.GroupBy, 430 | [ 431 | { type: "img"; src: string }, 432 | { type: "video"; src: 1 }, 433 | { type: "video"; src: 2 } 434 | ] 435 | >; 436 | type tes1 = Expect< 437 | Equal< 438 | res1, 439 | { 440 | img: [ 441 | { 442 | type: "img"; 443 | src: string; 444 | } 445 | ]; 446 | video: [ 447 | { 448 | type: "video"; 449 | src: 1; 450 | }, 451 | { 452 | type: "video"; 453 | src: 2; 454 | } 455 | ]; 456 | } 457 | > 458 | >; 459 | }); 460 | 461 | it("Range", () => { 462 | type res0 = Call, 7>; 463 | // ^? 464 | type test0 = Expect>; 465 | 466 | type res1 = Call, 5>; 467 | // ^? 468 | type test1 = Expect>; 469 | 470 | type res3 = Call>; 471 | // ^? 472 | type test3 = Expect>; 473 | 474 | type res4 = Call>; 475 | // ^? 476 | type test4 = Expect>; 477 | }); 478 | 479 | it("Min", () => { 480 | type res1 = Call>; 481 | // ^? 482 | type test1 = Expect>; 483 | 484 | type res2 = Call>; 485 | // ^? 486 | type test2 = Expect>; 487 | 488 | type res3 = Call>; 489 | // ^? 490 | type test3 = Expect>; 491 | }); 492 | 493 | it("Max", () => { 494 | type res1 = Call>; 495 | // ^? 496 | type test1 = Expect>; 497 | 498 | type res2 = Call>; 499 | // ^? 500 | type test2 = Expect>; 501 | 502 | type res3 = Call>; 503 | // ^? 504 | type test3 = Expect>; 505 | }); 506 | 507 | it("ToUnion", () => { 508 | type res1 = Call>; 509 | // ^? 510 | type test1 = Expect>; 511 | }); 512 | 513 | it("ToIntersection", () => { 514 | type res1 = Call; 515 | // ^? 516 | type test1 = Expect>; 517 | 518 | type res2 = Call; 519 | // ^? 520 | type test2 = Expect>; 521 | 522 | type res3 = Call; 523 | // ^? 524 | type test3 = Expect>; 525 | 526 | type res4 = Call; 527 | // ^? 528 | type test4 = Expect>; 529 | 530 | type res5 = Call< 531 | Tuples.ToIntersection, 532 | [string | number, string, "hello" | "hi"] 533 | >; 534 | // ^? 535 | type test5 = Expect>; 536 | }); 537 | 538 | it("Composition", () => { 539 | interface Duplicate extends Fn { 540 | return: [this["arg0"], this["arg0"]]; 541 | } 542 | 543 | // prettier-ignore 544 | type res = Pipe< 545 | // ^? 546 | [1, 2, 3, 4, 5, 5, 6], 547 | [ 548 | Tuples.FlatMap, 549 | Tuples.Map>, 550 | Tuples.Drop<3>, 551 | Tuples.Take<6>, 552 | Tuples.Sum 553 | ] 554 | >; 555 | 556 | type test = Expect>; 557 | 558 | type Factorial = Pipe< 559 | N, 560 | [Tuples.Range<1, _>, Tuples.Reduce] 561 | >; 562 | 563 | type res2 = Factorial<7>; 564 | // ^? 565 | type test2 = Expect>; 566 | 567 | type res3 = Factorial<9>; 568 | // ^? 569 | type test3 = Expect>; 570 | }); 571 | }); 572 | -------------------------------------------------------------------------------- /test/unions.test.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, Fn, B, U, Call, Unions, _ } from "../src/index"; 2 | import { Compose } from "../src/internals/core/Core"; 3 | import { Equal, Expect, Extends } from "../src/internals/helpers"; 4 | 5 | describe("Unions", () => { 6 | it("Exclude", () => { 7 | type res1 = Pipe<"a" | "b" | "c", [U.Exclude<"a">]>; 8 | // ^? 9 | type tes1 = Expect>; 10 | }); 11 | 12 | it("NonNullable", () => { 13 | type res1 = Pipe<"a" | 1 | null | undefined, [U.NonNullable]>; 14 | // ^? 15 | type tes1 = Expect>; 16 | type res2 = Call>; 17 | // ^? 18 | type tes2 = Expect>; 19 | type res3 = Call; 20 | // ^? 21 | type tes3 = Expect>; 22 | }); 23 | 24 | it("Extract", () => { 25 | type res1 = Pipe<"a" | "b" | "c", [U.Extract<"a" | "b">]>; 26 | type tes1 = Expect>; 27 | }); 28 | 29 | it("ExcludeBy", () => { 30 | type res1 = Pipe<"a" | "b" | "c", [U.ExcludeBy>]>; 31 | // ^? 32 | type tes1 = Expect>; 33 | }); 34 | 35 | it("ExtractBy", () => { 36 | type res1 = Pipe< 37 | // ^? 38 | "a" | "b" | "c", 39 | [U.ExtractBy]>>] 40 | >; 41 | type tes1 = Expect>; 42 | }); 43 | 44 | it("Map", () => { 45 | interface ToTuple extends Fn { 46 | return: [this["arg0"]]; 47 | } 48 | 49 | type res1 = Pipe<"a" | "b" | "c", [U.Map]>; 50 | // ^? 51 | type tes1 = Expect>; 52 | 53 | type res2 = Call>; 54 | // ^? 55 | type tes2 = Expect>; 56 | }); 57 | 58 | it("Range", () => { 59 | type res0 = Call, 7>; 60 | // ^? 61 | type test0 = Expect>; 62 | 63 | type res1 = Call, 5>; 64 | // ^? 65 | type test1 = Expect>; 66 | 67 | type res3 = Call>; 68 | // ^? 69 | type test3 = Expect>; 70 | 71 | type res4 = Call>; 72 | // ^? 73 | type test4 = Expect>; 74 | }); 75 | 76 | it("ToTuple", () => { 77 | type res1 = Call; 78 | // ^? 79 | // Since the order isn't stable we can't use `Equal`: 80 | type test1 = Expect>; 81 | type test2 = Expect>; 82 | }); 83 | 84 | it("ToIntersection", () => { 85 | type res1 = Call; 86 | // ^? 87 | type test1 = Expect>; 88 | 89 | type res2 = Call; 90 | // ^? 91 | type test2 = Expect>; 92 | 93 | type res3 = Call; 94 | // ^? 95 | type test3 = Expect>; 96 | 97 | type res4 = Call; 98 | // ^? 99 | type test4 = Expect>; 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ESNext", 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "target": "ESNext", 7 | "strict": true, 8 | "outDir": "dist/" 9 | }, 10 | "include": ["src/"], 11 | "exclude": ["tests/", "dist/", "examples/"] 12 | } 13 | --------------------------------------------------------------------------------