├── .eslintignore ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── arithmetic.ts ├── arrays.ts ├── definitions.ts ├── equality.ts ├── functions.ts ├── globals.ts ├── infer-return-type.ts ├── objects.ts ├── simple.ts └── variables.ts ├── images ├── example1.png └── example2.png ├── package.json ├── src ├── Checker │ ├── Checker.ts │ ├── MatchType.ts │ ├── TypeDefinitions.ts │ ├── Types.ts │ ├── Utils.ts │ └── index.ts ├── Parser │ ├── Ast.ts │ ├── Parser.ts │ ├── Utils.ts │ └── index.ts ├── Serializer │ ├── Serializer.ts │ └── index.ts ├── Tokenizer │ ├── Inputs.ts │ ├── Math.ts │ ├── StringUtils.ts │ ├── Tokenizer.ts │ ├── Tokens.ts │ └── index.ts ├── Utils │ ├── ArrayUtils.ts │ ├── Errors.ts │ ├── Format.ts │ ├── ObjectUtils.ts │ └── index.ts └── index.ts ├── test ├── Arrays.test.ts ├── Comparisons.test.ts ├── Conditionals.test.ts ├── Functions.test.ts ├── Objects.test.ts ├── TestUtils.ts └── Variables.test.ts ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "react-app", 4 | "prettier", 5 | "plugin:prettier/recommended", 6 | "plugin:@typescript-eslint/eslint-recommended" 7 | ], 8 | "settings": { 9 | "react": { 10 | // Fix for https://github.com/jaredpalmer/tsdx/issues/279 11 | "version": "999.999.999" 12 | } 13 | }, 14 | "rules": { 15 | "@typescript-eslint/no-unused-vars": "off", 16 | "import/no-anonymous-default-export": "off", 17 | "@typescript-eslint/prefer-ts-expect-error": "error", 18 | "@typescript-eslint/explicit-module-boundary-types": [ 19 | "error", 20 | { "allowArgumentsExplicitlyTypedAsAny": true } 21 | ], 22 | "no-eval": "off" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | build 4 | .eslintcache 5 | tsconfig.tsbuildinfo 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ronen Amiel 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. -------------------------------------------------------------------------------- /examples/arithmetic.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | 3 | // Hover over `Errors1` to see what's wrong with this input 4 | type Errors1 = TypeCheck<` 5 | 6 | // What's the type of 'a'? Try removing or changing the annotation to 7 | // fix the type error 8 | 9 | const a: string = 2 * 3; 10 | 11 | `>; 12 | 13 | // Hover over `Errors2` to see what's wrong with this input 14 | type Errors2 = TypeCheck<` 15 | 16 | // What about this computation? Should this show an error? 17 | const b = 3 / false; 18 | 19 | `>; 20 | -------------------------------------------------------------------------------- /examples/arrays.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | 3 | // Hover over `Errors1` to see what's wrong with this input 4 | type Errors1 = TypeCheck<` 5 | 6 | // Defining an array of numbers 7 | const a = [1, 2, 3]; 8 | 9 | // Changing one of its members to a string, will it work? 10 | a[1] = 'foo'; 11 | 12 | `>; 13 | 14 | // Hover over `Errors2` to see what's wrong with this input 15 | type Errors2 = TypeCheck<` 16 | 17 | // Defining another array of strings and numbers 18 | const b = [1, 'a', 2, 'b']; 19 | 20 | // Trying to assign one of its members to a value of type string, will it work? 21 | const value: string = b[0]; 22 | 23 | `>; 24 | 25 | // Hover over `Errors3` to see what's wrong with this input 26 | type Errors3 = TypeCheck<` 27 | 28 | // Defining an object 29 | const a = {hello: 'world'}; 30 | 31 | // Defining another object 32 | const b = {hello: 5}; 33 | 34 | // An array with both objects 35 | const c = [a, b]; 36 | 37 | // Accessing a member of the array and getting its property 38 | // What's the value of result? 39 | const result: string = c[1].hello 40 | 41 | `>; 42 | -------------------------------------------------------------------------------- /examples/definitions.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | 3 | // Hover over `Errors1` to see what's wrong with this input 4 | type Errors1 = TypeCheck<` 5 | 6 | // Checking if an element is included in the array 7 | const a = [1, 2, 'a', 'b'].includes('a'); 8 | 9 | `>; 10 | 11 | // Hover over `Errors2` to see what's wrong with this input 12 | type Errors2 = TypeCheck<` 13 | 14 | // Trying to push a string into an array of numbers 15 | [1, 2, 3].push('a'); 16 | 17 | `>; 18 | 19 | // Hover over `Errors3` to see what's wrong with this input 20 | type Errors3 = TypeCheck<` 21 | 22 | // Checking for the index of a substring 23 | const b = 'hello world'.indexOf(true); 24 | 25 | `>; 26 | 27 | // Hover over `Errors4` to see what's wrong with this input 28 | type Errors4 = TypeCheck<` 29 | 30 | // Trying to split a string but forgetting the separator 31 | const c = "a, b, c".split(); 32 | 33 | `>; 34 | -------------------------------------------------------------------------------- /examples/equality.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | 3 | // Hover over `Errors1` to see what's wrong with this input 4 | type Errors1 = TypeCheck<` 5 | 6 | // Define a string 7 | const hello = 'world'; 8 | 9 | // Define a number 10 | const foo = 5; 11 | 12 | // Can we compare them? What's the type of 'result'? 13 | const result = foo === hello; 14 | 15 | `>; 16 | 17 | // Hover over `Errors2` to see what's wrong with this input 18 | type Errors2 = TypeCheck<` 19 | 20 | // What about checking if two string literals are equal? 21 | if ('hello' == 'world') { 22 | console.log(1); 23 | } 24 | 25 | `>; 26 | -------------------------------------------------------------------------------- /examples/functions.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | 3 | // Hover over `Errors1` to see what's wrong with this input 4 | type Errors1 = TypeCheck<` 5 | 6 | // Define a function, what does this function return? 7 | function bar(a) { 8 | // (Try adding a type annotation on 'a' to fix the type error) 9 | return a; 10 | } 11 | 12 | `>; 13 | 14 | // Hover over `Errors2` to see what's wrong with this input 15 | type Errors2 = TypeCheck<` 16 | 17 | function bar(a: number) { 18 | return a; 19 | } 20 | 21 | // Not passing any arguments, should this show an error? 22 | const result1 = bar(); 23 | 24 | `>; 25 | 26 | // Hover over `Errors3` to see what's wrong with this input 27 | type Errors3 = TypeCheck<` 28 | 29 | function bar(a: number) { 30 | return a; 31 | } 32 | 33 | // Passing the wrong type of argument (string instead of a number) 34 | const result2 = bar('bazz'); 35 | 36 | `>; 37 | 38 | // Hover over `Errors4` to see what's wrong with this input 39 | type Errors4 = TypeCheck<` 40 | 41 | function bar(a: string) { 42 | return function () { 43 | return a.includes('hello'); 44 | }; 45 | } 46 | 47 | // Calling the function correctly and trying to match its value to 'boolean' 48 | const fn = bar(1); 49 | 50 | // Can 'fn' be called? Try below 51 | 52 | `>; 53 | -------------------------------------------------------------------------------- /examples/globals.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | 3 | // Hover over `Errors1` to see what's wrong with this input 4 | type Errors1 = TypeCheck<` 5 | 6 | // Try breaking this... and see what else is available 7 | 8 | // Using globally available console, limited to a single argument 9 | console.log(123); 10 | 11 | `>; 12 | 13 | // Hover over `Errors2` to see what's wrong with this input 14 | type Errors2 = TypeCheck<` 15 | 16 | // Try breaking this... and see what else is available 17 | 18 | // Or setting a timeout 19 | setTimeout(function () { 20 | console.log(1); 21 | }, 100); 22 | 23 | `>; 24 | 25 | // Hover over `Errors3` to see what's wrong with this input 26 | type Errors3 = TypeCheck<` 27 | 28 | // Try breaking this... and see what else is available 29 | 30 | // Or eval'ing 31 | eval("what is this even") 32 | 33 | `>; 34 | -------------------------------------------------------------------------------- /examples/infer-return-type.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | 3 | // Hover over `Errors1` to see what's wrong with this input 4 | type Errors1 = TypeCheck<` 5 | 6 | // Define a function, what does this function return? 7 | function bar(a: number) { 8 | if (a == 1) { 9 | return 'one'; 10 | } 11 | 12 | return a; 13 | } 14 | 15 | // Let's check its return value, will this show an error? 16 | const result1: number = bar(5); 17 | 18 | `>; 19 | 20 | // Hover over `Errors2` to see what's wrong with this input 21 | type Errors2 = TypeCheck<` 22 | 23 | // What about this one? 24 | 25 | // Define a function 26 | function foo(n: number) { 27 | return n; 28 | } 29 | 30 | // Define another one 31 | function bar(n: number) { 32 | if (n) { 33 | return n; 34 | } 35 | 36 | return 'oops!'; 37 | } 38 | 39 | // Create an array with both functions 40 | const array = [foo, bar]; 41 | 42 | // Try calling a member of the array, what would it return? 43 | const result: number = array[0](5) 44 | 45 | `>; 46 | -------------------------------------------------------------------------------- /examples/objects.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | 3 | // Hover over `Errors1` to see what's wrong with this input 4 | type Errors1 = TypeCheck<` 5 | 6 | // Defining an object 7 | const a = { 8 | hello: "world", 9 | foo: 5, 10 | }; 11 | 12 | // Changing one of its values from type number to type string, will it work? 13 | a.foo = 'world'; 14 | 15 | `>; 16 | 17 | // Hover over `Errors2` to see what's wrong with this input 18 | type Errors2 = TypeCheck<` 19 | 20 | const hello = 'world'; 21 | 22 | const a = { 23 | hello, 24 | foo: 5, 25 | }; 26 | 27 | // Creating another object that references 'a' 28 | const b = { 29 | a: a, 30 | }; 31 | 32 | // Trying to assign one of b's properties to a string, will it work? 33 | const c: string = b.a; 34 | 35 | `>; 36 | -------------------------------------------------------------------------------- /examples/simple.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | 3 | // Hover over the `Errors` type to see its value 4 | type Errors = TypeCheck<` 5 | 6 | function square(n: number) { 7 | return n * n; 8 | } 9 | 10 | square("2"); 11 | 12 | `>; 13 | -------------------------------------------------------------------------------- /examples/variables.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | 3 | // Hover over `Errors1` to see what's wrong with this input 4 | type Errors1 = TypeCheck<` 5 | 6 | // Define a constant 7 | const a = 5; 8 | 9 | // Can it be changed? 10 | a = 3; 11 | 12 | `>; 13 | 14 | // Hover over `Errors2` to see what's wrong with this input 15 | type Errors2 = TypeCheck<` 16 | 17 | // Define a let variable 18 | let b = "hello"; 19 | 20 | // Can it be changed? 21 | b = "world"; 22 | 23 | // What about changing its type? 24 | b = true; 25 | 26 | `>; 27 | 28 | // Hover over `Errors3` to see what's wrong with this input 29 | type Errors3 = TypeCheck<` 30 | 31 | // Now with type annotations 32 | let c: number = true; 33 | 34 | `>; 35 | 36 | // Hover over `Errors4` to see what's wrong with this input 37 | type Errors4 = TypeCheck<` 38 | 39 | // Even local references 40 | if (true) { 41 | let a = 5; 42 | 43 | // This works 44 | console.log(a); 45 | } 46 | 47 | // This doesn't :( 48 | console.log(a); 49 | 50 | `>; 51 | -------------------------------------------------------------------------------- /images/example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ronami/HypeScript/1b06248a48f33aa84c7a74876a020027fe696338/images/example1.png -------------------------------------------------------------------------------- /images/example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ronami/HypeScript/1b06248a48f33aa84c7a74876a020027fe696338/images/example2.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ronami/hypescript", 3 | "version": "1.2.0", 4 | "description": "A simplified implementation of TypeScript's type system written in TypeScript's own type system", 5 | "license": "MIT", 6 | "author": "Ronen Amiel", 7 | "types": "./build/src/index.d.ts", 8 | "files": ["build"], 9 | "scripts": { 10 | "watch": "tsc -w", 11 | "lint": "eslint . --ext js,ts,tsx --max-warnings 0" 12 | }, 13 | "devDependencies": { 14 | "@types/node": "^18.0.0", 15 | "@typescript-eslint/eslint-plugin": "^5.29.0", 16 | "@typescript-eslint/parser": "^5.29.0", 17 | "eslint": "^8.18.0", 18 | "eslint-config-prettier": "^8.5.0", 19 | "eslint-config-react-app": "^7.0.1", 20 | "eslint-plugin-flowtype": "^8.0.3", 21 | "eslint-plugin-import": "^2.26.0", 22 | "eslint-plugin-jsx-a11y": "^6.6.0", 23 | "eslint-plugin-prettier": "^4.0.0", 24 | "eslint-plugin-react": "^7.30.1", 25 | "eslint-plugin-react-hooks": "^4.6.0", 26 | "prettier": "^2.7.1", 27 | "typescript": "5.3.3" 28 | }, 29 | "prettier": { 30 | "printWidth": 80, 31 | "semi": true, 32 | "singleQuote": true, 33 | "trailingComma": "all" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Checker/Checker.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | AnyType, 3 | ArrayType, 4 | BooleanLiteralType, 5 | FunctionType, 6 | NeverType, 7 | NullType, 8 | NumberLiteralType, 9 | ObjectType, 10 | StaticType, 11 | StringLiteralType, 12 | UndefinedType, 13 | UnionType, 14 | UnknownType, 15 | VoidType, 16 | StateType, 17 | TypeResult, 18 | TypeArrayResult, 19 | MapLiteralToType, 20 | MatchType, 21 | GetObjectValueByKey, 22 | MapAnnotationToType, 23 | MergeTypes, 24 | StateVariableType, 25 | IsKindMutable, 26 | MergeFunctionTypesArray, 27 | BooleanType, 28 | MismatchBinaryErrorHelper, 29 | OverlapType, 30 | NumberType, 31 | IsNumeric, 32 | StringType, 33 | PropertyDoesNotExistResult, 34 | ArrayTypeMembers, 35 | StringTypeMembers, 36 | FunctionTypeMembers, 37 | GlobalTypeMembers, 38 | } from '.'; 39 | import type { 40 | ArrayExpression, 41 | BlockStatement, 42 | BooleanLiteral, 43 | CallExpression, 44 | ExpressionStatement, 45 | FunctionDeclaration, 46 | Identifier, 47 | MemberExpression, 48 | BaseNode, 49 | NodeData, 50 | NullLiteral, 51 | NumericLiteral, 52 | ObjectExpression, 53 | ObjectProperty, 54 | ReturnStatement, 55 | StringLiteral, 56 | TypeAnnotation, 57 | VariableDeclaration, 58 | VariableDeclarator, 59 | IfStatement, 60 | AssignmentExpression, 61 | BinaryExpression, 62 | FunctionExpression, 63 | } from '../Parser'; 64 | import type { Serialize } from '../Serializer'; 65 | import type { 66 | Concat, 67 | Push, 68 | Tail, 69 | Unshift, 70 | ObjectMerge, 71 | TypeError, 72 | } from '../Utils'; 73 | 74 | export type Check>> = InferBlockStatement< 75 | NodeList, 76 | GlobalTypeMembers 77 | > extends TypeResult 78 | ? Errors 79 | : never; 80 | 81 | type InferBlockStatement< 82 | NodeList extends Array>, 83 | State extends StateType, 84 | Result extends StaticType = NeverType, 85 | Errors extends Array> = [], 86 | > = NodeList extends [] 87 | ? MergeTypes extends infer ReturnType 88 | ? ReturnType extends StaticType 89 | ? TypeResult 90 | : never 91 | : never 92 | : NodeList[0] extends ExpressionStatement 93 | ? InferExpression extends TypeResult< 94 | any, 95 | infer ExpressionState, 96 | infer ExpressionErrors 97 | > 98 | ? InferBlockStatement< 99 | Tail, 100 | ExpressionState, 101 | Result, 102 | Concat 103 | > 104 | : never 105 | : NodeList[0] extends VariableDeclaration< 106 | [ 107 | VariableDeclarator< 108 | Identifier< 109 | infer Name, 110 | infer Annotation, 111 | NodeData 112 | >, 113 | infer Init, 114 | any 115 | >, 116 | ], 117 | infer Kind, 118 | any 119 | > 120 | ? InferVariableDeclaration< 121 | Name, 122 | Annotation, 123 | Init, 124 | Kind, 125 | State, 126 | StartLine 127 | > extends TypeResult 128 | ? InferBlockStatement< 129 | Tail, 130 | DeclarationState, 131 | Result, 132 | Concat 133 | > 134 | : never 135 | : NodeList[0] extends FunctionDeclaration< 136 | Identifier, 137 | infer Params, 138 | BlockStatement, 139 | any 140 | > 141 | ? InferFunctionDeclaration extends TypeResult< 142 | any, 143 | infer DeclarationState, 144 | infer DeclarationErrors 145 | > 146 | ? InferBlockStatement< 147 | Tail, 148 | DeclarationState, 149 | Result, 150 | Concat 151 | > 152 | : never 153 | : NodeList[0] extends ReturnStatement 154 | ? InferReturnStatement extends TypeResult< 155 | infer ReturnValue, 156 | infer ReturnState, 157 | infer ReturnErrors 158 | > 159 | ? MergeTypes extends infer ReturnType 160 | ? ReturnType extends StaticType 161 | ? TypeResult> 162 | : never 163 | : never 164 | : never 165 | : NodeList[0] extends IfStatement< 166 | infer Test, 167 | BlockStatement, 168 | any 169 | > 170 | ? InferExpression extends TypeResult< 171 | infer TestValue, 172 | infer TestState, 173 | infer TestErrors 174 | > 175 | ? InferBlockStatement extends TypeResult< 176 | infer IfStatementValue, 177 | any, 178 | infer IfStatementErrors 179 | > 180 | ? InferBlockStatement< 181 | Tail, 182 | TestState, 183 | MergeTypes extends infer ReturnType 184 | ? ReturnType extends StaticType 185 | ? IfStatementValue extends VoidType 186 | ? Result 187 | : ReturnType 188 | : never 189 | : never, 190 | [...Errors, ...TestErrors, ...IfStatementErrors] 191 | > 192 | : never 193 | : never 194 | : InferBlockStatement, State, Result, Errors>; 195 | 196 | type InferReturnStatement< 197 | ReturnExpression extends BaseNode | null, 198 | State extends StateType, 199 | > = ReturnExpression extends BaseNode 200 | ? InferExpression extends TypeResult< 201 | infer ExpressionValue, 202 | infer ExpressionState, 203 | infer ExpressionErrors 204 | > 205 | ? TypeResult< 206 | MapLiteralToType, 207 | ExpressionState, 208 | ExpressionErrors 209 | > 210 | : never 211 | : TypeResult; 212 | 213 | type InferFunctionParams< 214 | Params extends Array>, 215 | FunctionParams extends Array<[string, StaticType]> = [], 216 | ParamsByName extends StateType = {}, 217 | Errors extends Array> = [], 218 | > = Params extends [] 219 | ? [FunctionParams, ParamsByName, Errors] 220 | : Params[0] extends Identifier< 221 | infer Name, 222 | infer Annotation, 223 | NodeData 224 | > 225 | ? Annotation extends TypeAnnotation 226 | ? InferFunctionParamsHelper< 227 | Params, 228 | FunctionParams, 229 | ParamsByName, 230 | MapAnnotationToType, 231 | Name, 232 | Errors 233 | > 234 | : InferFunctionParamsHelper< 235 | Params, 236 | FunctionParams, 237 | ParamsByName, 238 | AnyType, 239 | Name, 240 | Push< 241 | Errors, 242 | TypeError< 243 | `Parameter '${Name}' implicitly has an 'any' type.`, 244 | LineNumber 245 | > 246 | > 247 | > 248 | : never; 249 | 250 | type InferFunctionParamsHelper< 251 | Params extends Array>, 252 | FunctionParams extends Array<[string, StaticType]>, 253 | ParamsByName extends StateType, 254 | Type extends StaticType, 255 | Name extends string, 256 | Errors extends Array>, 257 | > = InferFunctionParams< 258 | Tail, 259 | Push, 260 | ObjectMerge }>, 261 | Errors 262 | >; 263 | 264 | type InferFunctionDeclaration< 265 | Name extends string, 266 | Params extends Array>, 267 | Body extends Array>, 268 | State extends StateType, 269 | > = InferFunctionParams extends [ 270 | infer FunctionParams, 271 | infer ParamsByName, 272 | infer Errors, 273 | ] 274 | ? FunctionParams extends Array<[string, StaticType]> 275 | ? ParamsByName extends StateType 276 | ? Errors extends Array> 277 | ? InferBlockStatement< 278 | Body, 279 | ObjectMerge 280 | > extends TypeResult< 281 | infer BlockStatementReturnType, 282 | any, 283 | infer BlockStatementErrors 284 | > 285 | ? TypeResult< 286 | UndefinedType, 287 | ObjectMerge< 288 | State, 289 | { 290 | [a in Name]: StateVariableType< 291 | FunctionType, 292 | false 293 | >; 294 | } 295 | >, 296 | Concat 297 | > 298 | : never 299 | : never 300 | : never 301 | : never 302 | : never; 303 | 304 | type MatchCallExpressionArguments< 305 | ParamsType extends Array<[string, StaticType]>, 306 | ArgumentsType extends Array, 307 | StartLine extends number, 308 | > = ParamsType extends [] 309 | ? true 310 | : MatchType extends true 311 | ? MatchCallExpressionArguments< 312 | Tail, 313 | Tail, 314 | StartLine 315 | > 316 | : TypeError< 317 | `Argument of type '${Serialize< 318 | ArgumentsType[0] 319 | >}' is not assignable to parameter of type '${Serialize< 320 | ParamsType[0][1] 321 | >}'.`, 322 | StartLine 323 | >; 324 | 325 | type InferVariableDeclaration< 326 | Name extends string, 327 | Annotation extends BaseNode | null, 328 | Init extends BaseNode, 329 | Kind extends string, 330 | State extends StateType, 331 | StartLine extends number, 332 | > = InferExpression extends TypeResult< 333 | infer InitExpressionValue, 334 | infer InitExpressionState, 335 | infer InitExpressionErrors 336 | > 337 | ? Annotation extends TypeAnnotation 338 | ? MapAnnotationToType extends infer ExpectedType 339 | ? ExpectedType extends StaticType 340 | ? MatchType extends true 341 | ? TypeResult< 342 | UndefinedType, 343 | ObjectMerge< 344 | InitExpressionState, 345 | { 346 | [a in Name]: StateVariableType< 347 | ExpectedType, 348 | IsKindMutable 349 | >; 350 | } 351 | >, 352 | InitExpressionErrors 353 | > 354 | : TypeResult< 355 | UndefinedType, 356 | ObjectMerge< 357 | InitExpressionState, 358 | { 359 | [a in Name]: StateVariableType< 360 | ExpectedType, 361 | IsKindMutable 362 | >; 363 | } 364 | >, 365 | Push< 366 | InitExpressionErrors, 367 | TypeError< 368 | `Type '${Serialize}' is not assignable to type '${Serialize}'.`, 369 | StartLine 370 | > 371 | > 372 | > 373 | : never 374 | : never 375 | : TypeResult< 376 | UndefinedType, 377 | ObjectMerge< 378 | State, 379 | Kind extends 'const' 380 | ? { 381 | [a in Name]: StateVariableType; 382 | } 383 | : { 384 | [a in Name]: StateVariableType< 385 | MapLiteralToType, 386 | true 387 | >; 388 | } 389 | >, 390 | InitExpressionErrors 391 | > 392 | : never; 393 | 394 | type InferExpression< 395 | Node extends BaseNode, 396 | State extends StateType, 397 | > = Node extends StringLiteral 398 | ? TypeResult, State> 399 | : Node extends NumericLiteral 400 | ? TypeResult, State> 401 | : Node extends NullLiteral 402 | ? TypeResult 403 | : Node extends BooleanLiteral 404 | ? TypeResult, State> 405 | : Node extends Identifier> 406 | ? Name extends keyof State 407 | ? TypeResult 408 | : TypeResult< 409 | AnyType, 410 | State, 411 | [TypeError<`Cannot find name '${Name}'.`, StartLine>] 412 | > 413 | : Node extends FunctionExpression< 414 | any, 415 | infer Params, 416 | BlockStatement, 417 | any 418 | > 419 | ? InferFunctionExpression 420 | : Node extends ObjectExpression 421 | ? InferObjectProperties 422 | : Node extends MemberExpression< 423 | infer Object, 424 | infer Property, 425 | infer Computed, 426 | any 427 | > 428 | ? InferMemberExpression 429 | : Node extends ArrayExpression 430 | ? InferArrayElements 431 | : Node extends CallExpression< 432 | infer Callee, 433 | infer Arguments, 434 | NodeData 435 | > 436 | ? InferCallExpression 437 | : Node extends BinaryExpression< 438 | infer Left, 439 | infer Right, 440 | infer Operator, 441 | NodeData 442 | > 443 | ? InferBinaryExpression 444 | : Node extends AssignmentExpression< 445 | infer Left, 446 | infer Right, 447 | '=', 448 | NodeData 449 | > 450 | ? InferAssignmentExpression 451 | : UnknownType; 452 | 453 | type InferFunctionExpression< 454 | Params extends Array>, 455 | Body extends Array>, 456 | State extends StateType, 457 | > = InferFunctionParams extends [ 458 | infer FunctionParams, 459 | infer ParamsByName, 460 | infer Errors, 461 | ] 462 | ? FunctionParams extends Array<[string, StaticType]> 463 | ? ParamsByName extends StateType 464 | ? Errors extends Array> 465 | ? InferBlockStatement< 466 | Body, 467 | ObjectMerge 468 | > extends TypeResult< 469 | infer BlockStatementReturnType, 470 | any, 471 | infer BlockStatementErrors 472 | > 473 | ? TypeResult< 474 | FunctionType, 475 | State, 476 | Concat 477 | > 478 | : never 479 | : never 480 | : never 481 | : never 482 | : never; 483 | 484 | type InferBinaryExpression< 485 | Left extends BaseNode, 486 | Right extends BaseNode, 487 | State extends StateType, 488 | Operator extends string, 489 | LineNumber extends number, 490 | > = InferExpression extends TypeResult< 491 | infer LeftValue, 492 | infer LeftState, 493 | infer LeftErrors 494 | > 495 | ? InferExpression extends TypeResult< 496 | infer RightValue, 497 | infer RightState, 498 | infer RightErrors 499 | > 500 | ? Operator extends '==' | '===' 501 | ? InferComparisonExpression< 502 | RightValue, 503 | LeftValue, 504 | RightState, 505 | Concat, 506 | LineNumber 507 | > 508 | : Operator extends '+' | '-' | '*' | '/' 509 | ? InferArithmeticExpression< 510 | RightValue, 511 | LeftValue, 512 | RightState, 513 | Concat, 514 | LineNumber 515 | > 516 | : never 517 | : never 518 | : never; 519 | 520 | type InferComparisonExpression< 521 | RightValue extends StaticType, 522 | LeftValue extends StaticType, 523 | State extends StateType, 524 | Errors extends Array>, 525 | LineNumber extends number, 526 | > = OverlapType extends false 527 | ? TypeResult< 528 | BooleanType, 529 | State, 530 | MismatchBinaryErrorHelper 531 | > 532 | : TypeResult; 533 | 534 | type InferArithmeticExpression< 535 | RightValue extends StaticType, 536 | LeftValue extends StaticType, 537 | State extends StateType, 538 | Errors extends Array>, 539 | LineNumber extends number, 540 | > = MatchType extends true 541 | ? MatchType extends true 542 | ? TypeResult 543 | : TypeResult< 544 | NumberType, 545 | State, 546 | Push< 547 | Errors, 548 | TypeError< 549 | "The left-hand side of an arithmetic operation must be of type 'any' or 'number'.", 550 | LineNumber 551 | > 552 | > 553 | > 554 | : TypeResult< 555 | NumberType, 556 | State, 557 | Push< 558 | Errors, 559 | TypeError< 560 | "The right-hand side of an arithmetic operation must be of type 'any' or 'number'.", 561 | LineNumber 562 | > 563 | > 564 | >; 565 | 566 | type InferAssignmentExpression< 567 | Left extends BaseNode, 568 | Right extends BaseNode, 569 | State extends StateType, 570 | EqualsLineNumber extends number, 571 | > = InferExpression extends TypeResult< 572 | infer LeftValue, 573 | infer LeftState, 574 | infer LeftErrors 575 | > 576 | ? InferExpression extends TypeResult< 577 | infer RightValue, 578 | infer RightState, 579 | infer RightErrors 580 | > 581 | ? Left extends Identifier 582 | ? State[Name]['mutable'] extends false 583 | ? TypeResult< 584 | RightValue, 585 | RightState, 586 | Push< 587 | Concat, 588 | TypeError< 589 | `Cannot assign to '${Name}' because it is a constant.`, 590 | EqualsLineNumber 591 | > 592 | > 593 | > 594 | : InferAssignmentExpressionHelper< 595 | LeftValue, 596 | RightValue, 597 | RightState, 598 | Concat, 599 | EqualsLineNumber 600 | > 601 | : InferAssignmentExpressionHelper< 602 | LeftValue, 603 | RightValue, 604 | RightState, 605 | Concat, 606 | EqualsLineNumber 607 | > 608 | : never 609 | : never; 610 | 611 | type InferAssignmentExpressionHelper< 612 | LeftValue extends StaticType, 613 | RightValue extends StaticType, 614 | RightState extends StateType, 615 | Errors extends Array>, 616 | LineNumber extends number, 617 | > = MatchType extends true 618 | ? TypeResult 619 | : TypeResult< 620 | RightValue, 621 | RightState, 622 | Push< 623 | Errors, 624 | TypeError< 625 | `Type '${Serialize}' is not assignable to type '${Serialize}'.`, 626 | LineNumber 627 | > 628 | > 629 | >; 630 | 631 | type InferCallExpression< 632 | Callee extends BaseNode, 633 | Arguments extends Array>, 634 | State extends StateType, 635 | StartLine extends number, 636 | > = InferExpression extends TypeResult< 637 | infer CalleeValue, 638 | infer CalleeState, 639 | infer CalleeErrors 640 | > 641 | ? InferExpressionsArray extends TypeArrayResult< 642 | infer ArgumentsType, 643 | infer ArgumentsState, 644 | infer ArgumentsErrors 645 | > 646 | ? InferCallExpressionHelper< 647 | CalleeValue, 648 | ArgumentsType, 649 | ArgumentsState, 650 | Concat, 651 | StartLine 652 | > 653 | : never 654 | : never; 655 | 656 | type InferCallExpressionHelper< 657 | CalleeValue extends StaticType, 658 | ArgumentsType extends Array, 659 | State extends StateType, 660 | Errors extends Array>, 661 | StartLine extends number, 662 | > = CalleeValue extends FunctionType 663 | ? ParamsType['length'] extends ArgumentsType['length'] 664 | ? MatchCallExpressionArguments< 665 | ParamsType, 666 | ArgumentsType, 667 | StartLine 668 | > extends TypeError 669 | ? TypeResult< 670 | ReturnType, 671 | State, 672 | Push> 673 | > 674 | : TypeResult 675 | : TypeResult< 676 | ReturnType, 677 | State, 678 | Push< 679 | Errors, 680 | TypeError< 681 | `Expected ${ParamsType['length']} arguments, but got ${ArgumentsType['length']}.`, 682 | StartLine 683 | > 684 | > 685 | > 686 | : CalleeValue extends AnyType 687 | ? TypeResult 688 | : CalleeValue extends UnionType 689 | ? InferCallExpressionUnionHelper< 690 | CalleeValue, 691 | UnionTypes, 692 | ArgumentsType, 693 | State, 694 | StartLine, 695 | Errors 696 | > 697 | : TypeResult< 698 | AnyType, 699 | State, 700 | Unshift< 701 | Errors, 702 | TypeError< 703 | `This expression is not callable. Type '${Serialize}' has no call signatures.`, 704 | StartLine 705 | > 706 | > 707 | >; 708 | 709 | type InferCallExpressionUnionHelper< 710 | CalleeValue extends UnionType, 711 | UnionTypes extends Array, 712 | ArgumentsType extends Array, 713 | State extends StateType, 714 | StartLine extends number, 715 | Errors extends Array>, 716 | > = UnionTypes extends Array> 717 | ? InferCallExpressionHelper< 718 | MergeFunctionTypesArray, UnionTypes[0]>, 719 | ArgumentsType, 720 | State, 721 | Errors, 722 | StartLine 723 | > 724 | : TypeResult< 725 | AnyType, 726 | State, 727 | Push< 728 | Errors, 729 | TypeError< 730 | `This expression is not callable. Not all constituents of type '${Serialize}' are callable.`, 731 | StartLine 732 | > 733 | > 734 | >; 735 | 736 | type InferExpressionsArray< 737 | NodeList extends Array>, 738 | State extends StateType, 739 | Result extends Array = [], 740 | Errors extends Array> = [], 741 | > = NodeList extends [] 742 | ? TypeArrayResult 743 | : InferExpression extends TypeResult< 744 | infer ExpressionValue, 745 | infer ExpressionState, 746 | infer ExpressionErrors 747 | > 748 | ? InferExpressionsArray< 749 | Tail, 750 | ObjectMerge, 751 | Push, 752 | Concat 753 | > 754 | : never; 755 | 756 | type InferArrayElements< 757 | Elements extends Array>, 758 | State extends StateType, 759 | First extends boolean = true, 760 | Result extends StaticType = AnyType, 761 | Errors extends Array> = [], 762 | > = Elements extends [] 763 | ? TypeResult, State, Errors> 764 | : Elements[0] extends BaseNode 765 | ? InferExpression extends TypeResult< 766 | infer ExpressionValue, 767 | infer ExpressionState, 768 | infer ExpressionErrors 769 | > 770 | ? MapLiteralToType extends infer LiteralType 771 | ? LiteralType extends StaticType 772 | ? MergeTypes extends infer ReturnType 773 | ? ReturnType extends StaticType 774 | ? InferArrayElements< 775 | Tail, 776 | ExpressionState, 777 | false, 778 | First extends true ? LiteralType : ReturnType, 779 | Concat 780 | > 781 | : never 782 | : never 783 | : never 784 | : never 785 | : never 786 | : never; 787 | 788 | type InferMemberExpression< 789 | Object extends BaseNode, 790 | Property extends BaseNode, 791 | Computed extends boolean, 792 | State extends StateType, 793 | > = InferExpression extends TypeResult< 794 | infer ObjectExpressionValue, 795 | infer ObjectExpressionState, 796 | infer ObjectExpressionErrors 797 | > 798 | ? Computed extends false 799 | ? Property extends Identifier< 800 | infer Name, 801 | any, 802 | NodeData 803 | > 804 | ? InferMemberExpressionHelper< 805 | ObjectExpressionValue, 806 | Name, 807 | ObjectExpressionState, 808 | StartLine, 809 | ObjectExpressionErrors 810 | > 811 | : never 812 | : InferExpression extends TypeResult< 813 | infer PropertyExpressionValue, 814 | infer PropertyExpressionState, 815 | infer PropertyExpressionErrors 816 | > 817 | ? Property extends BaseNode> 818 | ? PropertyExpressionValue extends 819 | | StringLiteralType 820 | | NumberLiteralType 821 | ? InferMemberExpressionHelper< 822 | ObjectExpressionValue, 823 | Value, 824 | PropertyExpressionState, 825 | StartLine, 826 | Concat 827 | > 828 | : PropertyExpressionValue extends AnyType 829 | ? TypeResult< 830 | AnyType, 831 | PropertyExpressionState, 832 | Concat 833 | > 834 | : TypeResult< 835 | AnyType, 836 | PropertyExpressionState, 837 | Push< 838 | Concat, 839 | TypeError< 840 | `Type '${Serialize}' cannot be used as an index type.`, 841 | StartLine 842 | > 843 | > 844 | > 845 | : never 846 | : never 847 | : never; 848 | 849 | type InferMemberExpressionHelper< 850 | Object extends StaticType, 851 | Key extends string, 852 | State extends StateType, 853 | StartLine extends number, 854 | Errors extends Array>, 855 | > = Object extends ObjectType 856 | ? GetObjectValueByKey< 857 | ObjectProperties, 858 | Key 859 | > extends infer MemberExpressionValue 860 | ? MemberExpressionValue extends StaticType 861 | ? TypeResult 862 | : PropertyDoesNotExistResult< 863 | State, 864 | Errors, 865 | Key, 866 | Object, 867 | StartLine, 868 | UndefinedType 869 | > 870 | : never 871 | : Object extends ArrayType 872 | ? IsNumeric extends true 873 | ? TypeResult 874 | : Key extends keyof ArrayTypeMembers 875 | ? TypeResult[Key], State, Errors> 876 | : PropertyDoesNotExistResult 877 | : Object extends StringType | StringLiteralType 878 | ? Key extends keyof StringTypeMembers 879 | ? TypeResult 880 | : PropertyDoesNotExistResult 881 | : Object extends FunctionType 882 | ? Key extends keyof FunctionTypeMembers 883 | ? TypeResult 884 | : PropertyDoesNotExistResult 885 | : Object extends UnionType 886 | ? InferMemberExpressionUnionHelper 887 | : Object extends AnyType 888 | ? TypeResult 889 | : PropertyDoesNotExistResult; 890 | 891 | type InferMemberExpressionUnionHelper< 892 | UnionTypes extends Array, 893 | Key extends string, 894 | State extends StateType, 895 | StartLine extends number, 896 | Errors extends Array>, 897 | Result extends StaticType = NeverType, 898 | > = UnionTypes extends [] 899 | ? TypeResult 900 | : InferMemberExpressionHelper< 901 | UnionTypes[0], 902 | Key, 903 | State, 904 | StartLine, 905 | [] 906 | > extends TypeResult< 907 | infer ExpressionValue, 908 | infer ExpressionState, 909 | infer ExpressionErrors 910 | > 911 | ? MergeTypes extends infer ReturnType 912 | ? ReturnType extends StaticType 913 | ? InferMemberExpressionUnionHelper< 914 | Tail, 915 | Key, 916 | ExpressionState, 917 | StartLine, 918 | Errors, 919 | ReturnType 920 | > 921 | : never 922 | : never 923 | : never; 924 | 925 | type InferObjectProperties< 926 | Properties extends Array>, 927 | State extends StateType, 928 | Result extends Array = [], 929 | Errors extends Array> = [], 930 | > = Properties extends [] 931 | ? TypeResult, State, Errors> 932 | : Properties[0] extends ObjectProperty< 933 | Identifier, 934 | infer Value, 935 | any 936 | > 937 | ? InferExpression extends TypeResult< 938 | infer ExpressionValue, 939 | infer ExpressionState, 940 | infer ExpressionErrors 941 | > 942 | ? InferObjectProperties< 943 | Tail, 944 | ExpressionState, 945 | Push]>, 946 | Concat 947 | > 948 | : never 949 | : never; 950 | -------------------------------------------------------------------------------- /src/Checker/MatchType.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | AnyType, 3 | BooleanLiteralType, 4 | BooleanType, 5 | NeverType, 6 | NumberLiteralType, 7 | NumberType, 8 | StaticType, 9 | StringLiteralType, 10 | StringType, 11 | UnionType, 12 | ArrayType, 13 | ObjectType, 14 | FunctionType, 15 | GetObjectValueByKey, 16 | } from '.'; 17 | import type { Tail } from '../Utils'; 18 | 19 | export type OverlapType< 20 | TypeA extends StaticType, 21 | TypeB extends StaticType, 22 | > = TypeA extends ArrayType 23 | ? TypeB extends ArrayType 24 | ? OverlapType 25 | : false 26 | : TypeA extends ObjectType 27 | ? TypeB extends ObjectType 28 | ? OverlapObjectProperties 29 | : false 30 | : TypeA extends UnionType 31 | ? TypeB extends UnionType 32 | ? UnionsOverlap 33 | : TypeMatchUnion 34 | : TypeB extends UnionType 35 | ? TypeMatchUnion 36 | : MatchPrimitive extends true 37 | ? true 38 | : MatchPrimitive extends true 39 | ? true 40 | : false; 41 | 42 | export type MatchType< 43 | TypeA extends StaticType, 44 | TypeB extends StaticType, 45 | > = TypeA extends ArrayType 46 | ? TypeB extends ArrayType 47 | ? MatchType 48 | : false 49 | : TypeA extends ObjectType 50 | ? TypeB extends ObjectType 51 | ? MatchObjectProperties 52 | : false 53 | : TypeA extends FunctionType 54 | ? TypeB extends FunctionType 55 | ? MatchFunction 56 | : false 57 | : TypeA extends UnionType 58 | ? TypeB extends UnionType 59 | ? UnionMatchUnion 60 | : TypeMatchUnion 61 | : TypeB extends UnionType 62 | ? UnionMatchType 63 | : MatchPrimitive extends true 64 | ? true 65 | : false; 66 | 67 | type MatchFunction< 68 | ParamsA extends Array<[string, StaticType]>, 69 | ParamsB extends Array<[string, StaticType]>, 70 | ReturnA extends StaticType, 71 | ReturnB extends StaticType, 72 | > = ParamsA extends [] 73 | ? ParamsB extends [] 74 | ? MatchType 75 | : false 76 | : ParamsB extends [] 77 | ? false 78 | : MatchType extends true 79 | ? MatchFunction, Tail, ReturnA, ReturnB> 80 | : false; 81 | 82 | type MatchObjectProperties< 83 | PropertiesA extends Array<[string, StaticType]>, 84 | PropertiesB extends Array<[string, StaticType]>, 85 | Length extends number = PropertiesA['length'], 86 | > = PropertiesA extends [] 87 | ? PropertiesB['length'] extends Length 88 | ? true 89 | : false 90 | : PropertiesB extends [] 91 | ? false 92 | : MatchType< 93 | GetObjectValueByKey, 94 | GetObjectValueByKey 95 | > extends true 96 | ? MatchObjectProperties, PropertiesB, Length> 97 | : false; 98 | 99 | type OverlapObjectProperties< 100 | PropertiesA extends Array<[string, StaticType]>, 101 | PropertiesB extends Array<[string, StaticType]>, 102 | Length extends number = PropertiesA['length'], 103 | > = PropertiesA extends [] 104 | ? PropertiesB['length'] extends Length 105 | ? true 106 | : false 107 | : PropertiesB extends [] 108 | ? false 109 | : OverlapType< 110 | GetObjectValueByKey, 111 | GetObjectValueByKey 112 | > extends true 113 | ? OverlapObjectProperties, PropertiesB, Length> 114 | : false; 115 | 116 | type MatchPrimitive< 117 | TypeA extends StaticType, 118 | TypeB extends StaticType, 119 | > = TypeA extends NeverType 120 | ? false 121 | : TypeB extends NeverType 122 | ? false 123 | : TypeA extends AnyType 124 | ? true 125 | : TypeB extends AnyType 126 | ? true 127 | : TypeA extends TypeB 128 | ? TypeB extends TypeA 129 | ? true 130 | : false 131 | : TypeA extends StringType 132 | ? TypeB extends StringLiteralType 133 | ? true 134 | : false 135 | : TypeA extends BooleanType 136 | ? TypeB extends BooleanLiteralType 137 | ? true 138 | : false 139 | : TypeA extends NumberType 140 | ? TypeB extends NumberLiteralType 141 | ? true 142 | : false 143 | : false; 144 | 145 | type UnionsOverlap< 146 | UnionTypesA extends Array, 147 | UnionTypesB extends Array, 148 | > = UnionTypesB extends [] 149 | ? false 150 | : TypeMatchUnion extends false 151 | ? UnionMatchUnion> 152 | : true; 153 | 154 | type UnionMatchUnion< 155 | UnionTypesA extends Array, 156 | UnionTypesB extends Array, 157 | > = UnionTypesB extends [] 158 | ? true 159 | : TypeMatchUnion extends true 160 | ? UnionMatchUnion> 161 | : false; 162 | 163 | type TypeMatchUnion< 164 | UnionTypes extends Array, 165 | Type extends StaticType, 166 | > = UnionTypes extends [] 167 | ? false 168 | : MatchType extends true 169 | ? true 170 | : TypeMatchUnion, Type>; 171 | 172 | type UnionMatchType< 173 | Type extends StaticType, 174 | UnionTypes extends Array, 175 | > = UnionTypes extends [] 176 | ? true 177 | : MatchType extends true 178 | ? UnionMatchType> 179 | : false; 180 | -------------------------------------------------------------------------------- /src/Checker/TypeDefinitions.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | NumberType, 3 | StringType, 4 | BooleanType, 5 | FunctionType, 6 | ArrayType, 7 | StaticType, 8 | AnyType, 9 | UndefinedType, 10 | MergeTypes, 11 | ObjectType, 12 | StateVariableType, 13 | VoidType, 14 | } from '.'; 15 | 16 | export type GlobalTypeMembers = { 17 | console: StateVariableType< 18 | ObjectType<[['log', FunctionType<[['data', AnyType]], UndefinedType>]]>, 19 | true 20 | >; 21 | eval: StateVariableType, false>; 22 | setTimeout: StateVariableType< 23 | FunctionType< 24 | [['handler', FunctionType<[], VoidType>], ['timeout', NumberType]], 25 | NumberType 26 | >, 27 | false 28 | >; 29 | }; 30 | 31 | export type StringTypeMembers = { 32 | length: NumberType; 33 | includes: FunctionType<[['searchString', StringType]], BooleanType>; 34 | charAt: FunctionType<[['pos', NumberType]], StringType>; 35 | indexOf: FunctionType<[['searchString', StringType]], NumberType>; 36 | startsWith: FunctionType<[['searchString', StringType]], BooleanType>; 37 | endsWith: FunctionType<[['searchString', StringType]], BooleanType>; 38 | split: FunctionType<[['separator', StringType]], ArrayType>; 39 | replace: FunctionType< 40 | [['searchValue', StringType], ['replaceValue', StringType]], 41 | StringType 42 | >; 43 | }; 44 | 45 | export type ArrayTypeMembers = { 46 | length: NumberType; 47 | fill: FunctionType<[['value', ElementsType]], ArrayType>; 48 | reverse: FunctionType<[], ArrayType>; 49 | includes: FunctionType<[['searchElement', ElementsType]], BooleanType>; 50 | join: FunctionType<[['separator', StringType]], StringType>; 51 | indexOf: FunctionType<[['searchElement', ElementsType]], NumberType>; 52 | push: FunctionType<[['item', ElementsType]], NumberType>; 53 | pop: MergeTypes extends infer ReturnType 54 | ? ReturnType extends StaticType 55 | ? FunctionType<[], ReturnType> 56 | : never 57 | : never; 58 | unshift: FunctionType<[['item', ElementsType]], NumberType>; 59 | }; 60 | 61 | export type FunctionTypeMembers = { 62 | length: NumberType; 63 | name: StringType; 64 | prototype: AnyType; 65 | toString: FunctionType<[], StringType>; 66 | }; 67 | -------------------------------------------------------------------------------- /src/Checker/Types.ts: -------------------------------------------------------------------------------- 1 | export type StringType = { 2 | type: 'StringType'; 3 | }; 4 | 5 | export type StringLiteralType = { 6 | type: 'StringLiteralType'; 7 | value: Value; 8 | }; 9 | 10 | export type NumberType = { 11 | type: 'NumberType'; 12 | }; 13 | 14 | export type NumberLiteralType = { 15 | type: 'NumberLiteralType'; 16 | value: Value; 17 | }; 18 | 19 | export type BooleanType = { 20 | type: 'BooleanType'; 21 | }; 22 | 23 | export type BooleanLiteralType = { 24 | type: 'BooleanLiteralType'; 25 | value: Value; 26 | }; 27 | 28 | export type NullType = { 29 | type: 'NullType'; 30 | }; 31 | 32 | export type UndefinedType = { 33 | type: 'UndefinedType'; 34 | }; 35 | 36 | export type UnknownType = { 37 | type: 'UnknownType'; 38 | }; 39 | 40 | export type VoidType = { 41 | type: 'VoidType'; 42 | }; 43 | 44 | export type AnyType = { 45 | type: 'AnyType'; 46 | }; 47 | 48 | export type NeverType = { 49 | type: 'NeverType'; 50 | }; 51 | 52 | export type FunctionType< 53 | Params extends Array<[string, StaticType]>, 54 | Return extends StaticType, 55 | > = { 56 | type: 'FunctionType'; 57 | params: Params; 58 | return: Return; 59 | }; 60 | 61 | export type ObjectType> = { 62 | type: 'ObjectType'; 63 | properties: Properties; 64 | }; 65 | 66 | export type ArrayType = { 67 | type: 'ArrayType'; 68 | elements: ElementsType; 69 | }; 70 | 71 | export type UnionType> = { 72 | type: 'UnionType'; 73 | types: Types; 74 | }; 75 | 76 | export type StaticType = 77 | | StringType 78 | | StringLiteralType 79 | | NumberType 80 | | NumberLiteralType 81 | | BooleanType 82 | | BooleanLiteralType 83 | | UnknownType 84 | | VoidType 85 | | AnyType 86 | | NullType 87 | | UndefinedType 88 | | NeverType 89 | | FunctionType 90 | | ObjectType 91 | | ArrayType 92 | | UnionType; 93 | -------------------------------------------------------------------------------- /src/Checker/Utils.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | MatchType, 3 | StaticType, 4 | NumberLiteralType, 5 | NumberType, 6 | StringLiteralType, 7 | StringType, 8 | BooleanLiteralType, 9 | BooleanType, 10 | NullType, 11 | AnyType, 12 | UnknownType, 13 | NeverType, 14 | UnionType, 15 | FunctionType, 16 | } from '.'; 17 | import type { 18 | AnyTypeAnnotation, 19 | BaseNode, 20 | BooleanTypeAnnotation, 21 | NullLiteralTypeAnnotation, 22 | NumberTypeAnnotation, 23 | StringTypeAnnotation, 24 | } from '../Parser'; 25 | import type { Numbers } from '../Tokenizer'; 26 | import type { Serialize } from '../Serializer'; 27 | import type { Concat, Push, Tail, TypeError } from '../Utils'; 28 | 29 | export type StateVariableType< 30 | Value extends StaticType, 31 | Mutable extends boolean, 32 | > = { 33 | type: 'ConstVariableType'; 34 | value: Value; 35 | mutable: Mutable; 36 | }; 37 | 38 | export type StateType = Record>; 39 | 40 | export type TypeResult< 41 | Value extends StaticType, 42 | State extends StateType, 43 | Errors extends Array> = [], 44 | > = { 45 | type: 'TypeResult'; 46 | value: Value; 47 | state: State; 48 | errors: Errors; 49 | }; 50 | 51 | export type TypeArrayResult< 52 | TypeList extends Array, 53 | State extends StateType, 54 | Errors extends Array> = [], 55 | > = { 56 | type: 'TypeResult'; 57 | value: TypeList; 58 | state: State; 59 | errors: Errors; 60 | }; 61 | 62 | export type MapLiteralToType = 63 | Type extends NumberLiteralType 64 | ? NumberType 65 | : Type extends StringLiteralType 66 | ? StringType 67 | : Type extends BooleanLiteralType 68 | ? BooleanType 69 | : Type; 70 | 71 | export type GetObjectValueByKey< 72 | ObjectProperties extends Array<[string, StaticType]>, 73 | Key extends string, 74 | > = ObjectProperties extends [] 75 | ? null 76 | : ObjectProperties[0] extends [infer PropertyName, infer PropertyValue] 77 | ? PropertyName extends Key 78 | ? PropertyValue 79 | : GetObjectValueByKey, Key> 80 | : never; 81 | 82 | export type MapAnnotationToType> = 83 | AnnotationValue extends StringTypeAnnotation 84 | ? StringType 85 | : AnnotationValue extends NumberTypeAnnotation 86 | ? NumberType 87 | : AnnotationValue extends BooleanTypeAnnotation 88 | ? BooleanType 89 | : AnnotationValue extends NullLiteralTypeAnnotation 90 | ? NullType 91 | : AnnotationValue extends AnyTypeAnnotation 92 | ? AnyType 93 | : UnknownType; 94 | 95 | export type MergeTypes< 96 | TypeA extends StaticType, 97 | TypeB extends StaticType, 98 | > = TypeA extends NeverType 99 | ? TypeB 100 | : TypeB extends NeverType 101 | ? TypeA 102 | : TypeA extends AnyType 103 | ? AnyType 104 | : TypeB extends AnyType 105 | ? AnyType 106 | : MatchType extends true 107 | ? TypeA 108 | : MatchType extends true 109 | ? TypeB 110 | : TypeA extends UnionType 111 | ? TypeB extends UnionType 112 | ? UnionType> 113 | : UnionType> 114 | : TypeB extends UnionType 115 | ? UnionType> 116 | : UnionType<[TypeA, TypeB]>; 117 | 118 | export type IsKindMutable = Kind extends 'const' 119 | ? false 120 | : true; 121 | 122 | export type MergeFunctionTypesArray< 123 | FunctionTypes extends Array>, 124 | ReturnType extends FunctionType, 125 | > = FunctionTypes extends [] 126 | ? ReturnType 127 | : FunctionTypes[0] extends FunctionType 128 | ? MergeFunctionTypesArray< 129 | Tail, 130 | MergeFunctionTypes 131 | > 132 | : never; 133 | 134 | type MergeFunctionTypes< 135 | Params extends Array<[string, StaticType]>, 136 | Return extends StaticType, 137 | Function extends FunctionType, 138 | > = Function extends FunctionType 139 | ? MergeFunctionParams extends infer P 140 | ? P extends Array<[string, StaticType]> 141 | ? MergeTypes extends infer ReturnType 142 | ? ReturnType extends StaticType 143 | ? FunctionType 144 | : never 145 | : never 146 | : never 147 | : never 148 | : never; 149 | 150 | type MergeFunctionParams< 151 | ParamsA extends Array<[string, StaticType]>, 152 | ParamsB extends Array<[string, StaticType]>, 153 | Return extends Array<[string, StaticType]> = [], 154 | > = ParamsA extends [] 155 | ? ParamsB extends [] 156 | ? Return 157 | : Concat 158 | : ParamsB extends [] 159 | ? Concat 160 | : MatchType extends true 161 | ? MergeFunctionParams, Tail, Push> 162 | : MatchType extends true 163 | ? MergeFunctionParams, Tail, Push> 164 | : MergeFunctionParams< 165 | Tail, 166 | Tail, 167 | Push 168 | >; 169 | 170 | export type MismatchBinaryErrorHelper< 171 | Left extends StaticType, 172 | Right extends StaticType, 173 | LineNumber extends number, 174 | Errors extends Array>, 175 | ShouldMapLiterals extends boolean = IsSameLiteralType, 176 | > = Push< 177 | Errors, 178 | TypeError< 179 | `This condition will always return 'false' since the types '${Serialize< 180 | Left, 181 | ShouldMapLiterals 182 | >}' and '${Serialize}' have no overlap.`, 183 | LineNumber 184 | > 185 | >; 186 | 187 | type IsSameLiteralType = 188 | RightValue extends StringLiteralType 189 | ? LeftValue extends StringLiteralType 190 | ? true 191 | : false 192 | : RightValue extends NumberLiteralType 193 | ? LeftValue extends NumberLiteralType 194 | ? true 195 | : false 196 | : RightValue extends BooleanLiteralType 197 | ? LeftValue extends BooleanLiteralType 198 | ? true 199 | : false 200 | : false; 201 | 202 | export type PropertyDoesNotExistResult< 203 | State extends StateType, 204 | Errors extends Array>, 205 | Key extends string, 206 | Object extends StaticType, 207 | LineNumber extends number, 208 | Value extends StaticType = AnyType, 209 | > = TypeResult< 210 | Value, 211 | State, 212 | Push< 213 | Errors, 214 | TypeError< 215 | `Property '${Key}' does not exist on type '${Serialize}'.`, 216 | LineNumber 217 | > 218 | > 219 | >; 220 | 221 | export type IsNumeric = Input extends '' 222 | ? true 223 | : Input extends `${Numbers}${infer Rest}` 224 | ? IsNumeric 225 | : false; 226 | -------------------------------------------------------------------------------- /src/Checker/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Checker'; 2 | export * from './Types'; 3 | export * from './Utils'; 4 | export * from './MatchType'; 5 | export * from './TypeDefinitions'; 6 | -------------------------------------------------------------------------------- /src/Parser/Ast.ts: -------------------------------------------------------------------------------- 1 | export type NodeData = { 2 | startLineNumber: StartLine; 3 | endLineNumber: EndLine; 4 | }; 5 | 6 | export type NullLiteral> = { 7 | type: 'NullLiteral'; 8 | data: Data; 9 | }; 10 | 11 | export type NumericLiteral< 12 | Value extends string, 13 | Data extends NodeData, 14 | > = { 15 | type: 'NumericLiteral'; 16 | value: Value; 17 | data: Data; 18 | }; 19 | 20 | export type BooleanLiteral< 21 | Value extends boolean, 22 | Data extends NodeData, 23 | > = { 24 | type: 'BooleanLiteral'; 25 | value: Value; 26 | data: Data; 27 | }; 28 | 29 | export type StringLiteral< 30 | Value extends string, 31 | Data extends NodeData, 32 | > = { 33 | type: 'StringLiteral'; 34 | value: Value; 35 | data: Data; 36 | }; 37 | 38 | export type ArrayExpression< 39 | Elements extends Array>, 40 | Data extends NodeData, 41 | > = { 42 | type: 'ArrayExpression'; 43 | elements: Elements; 44 | data: Data; 45 | }; 46 | 47 | export type ObjectExpression< 48 | Properties extends Array>, 49 | Data extends NodeData, 50 | > = { 51 | type: 'ObjectExpression'; 52 | properties: Properties; 53 | data: Data; 54 | }; 55 | 56 | export type ObjectProperty< 57 | Key extends BaseNode, 58 | Value extends BaseNode, 59 | Data extends NodeData, 60 | > = { 61 | type: 'ObjectProperty'; 62 | key: Key; 63 | value: Value; 64 | data: Data; 65 | }; 66 | 67 | export type VariableDeclaration< 68 | Declarations extends Array>, 69 | Kind extends string, 70 | Data extends NodeData, 71 | > = { 72 | type: 'VariableDeclaration'; 73 | declarations: Declarations; 74 | kind: Kind; 75 | data: Data; 76 | }; 77 | 78 | export type VariableDeclarator< 79 | Id extends BaseNode, 80 | Init extends BaseNode, 81 | Data extends NodeData, 82 | > = { 83 | type: 'VariableDeclarator'; 84 | id: Id; 85 | init: Init; 86 | data: Data; 87 | }; 88 | 89 | export type FunctionDeclaration< 90 | Id extends BaseNode, 91 | Params extends Array>, 92 | Body extends BaseNode, 93 | Data extends NodeData, 94 | > = { 95 | type: 'FunctionDeclaration'; 96 | id: Id; 97 | params: Params; 98 | body: Body; 99 | data: Data; 100 | }; 101 | 102 | export type FunctionExpression< 103 | Id extends BaseNode | null, 104 | Params extends Array>, 105 | Body extends BaseNode, 106 | Data extends NodeData, 107 | > = { 108 | type: 'FunctionExpression'; 109 | id: Id; 110 | params: Params; 111 | body: Body; 112 | data: Data; 113 | }; 114 | 115 | export type Identifier< 116 | Name extends string, 117 | Annotation extends BaseNode | null, 118 | Data extends NodeData, 119 | > = { 120 | type: 'Identifier'; 121 | name: Name; 122 | typeAnnotation: Annotation; 123 | data: Data; 124 | }; 125 | 126 | export type ExpressionStatement< 127 | Expression extends BaseNode, 128 | Data extends NodeData, 129 | > = { 130 | type: 'ExpressionStatement'; 131 | expression: Expression; 132 | data: Data; 133 | }; 134 | 135 | export type CallExpression< 136 | Callee extends BaseNode, 137 | Arguments extends Array>, 138 | Data extends NodeData, 139 | > = { 140 | type: 'CallExpression'; 141 | callee: Callee; 142 | arguments: Arguments; 143 | data: Data; 144 | }; 145 | 146 | export type MemberExpression< 147 | Object extends BaseNode, 148 | Property extends BaseNode, 149 | Computed extends boolean, 150 | Data extends NodeData, 151 | > = { 152 | type: 'MemberExpression'; 153 | object: Object; 154 | property: Property; 155 | computed: Computed; 156 | data: Data; 157 | }; 158 | 159 | export type IfStatement< 160 | Test extends BaseNode, 161 | Consequent extends BaseNode, 162 | Data extends NodeData, 163 | > = { 164 | type: 'IfStatement'; 165 | test: Test; 166 | consequent: Consequent; 167 | data: Data; 168 | // alternate: A; 169 | }; 170 | 171 | export type ReturnStatement< 172 | Argument extends BaseNode | null, 173 | Data extends NodeData, 174 | > = { 175 | type: 'ReturnStatement'; 176 | argument: Argument; 177 | data: Data; 178 | }; 179 | 180 | export type AssignmentExpression< 181 | Left extends BaseNode, 182 | Right extends BaseNode, 183 | Operator extends string, 184 | Data extends NodeData, 185 | > = { 186 | type: 'AssignmentExpression'; 187 | left: Left; 188 | right: Right; 189 | operator: Operator; 190 | data: Data; 191 | }; 192 | 193 | export type BinaryExpression< 194 | Left extends BaseNode, 195 | Right extends BaseNode, 196 | Operator extends string, 197 | Data extends NodeData, 198 | > = { 199 | type: 'BinaryExpression'; 200 | left: Left; 201 | right: Right; 202 | operator: Operator; 203 | data: Data; 204 | }; 205 | 206 | export type BlockStatement< 207 | Body extends Array>, 208 | Data extends NodeData, 209 | > = { 210 | type: 'BlockStatement'; 211 | body: Body; 212 | data: Data; 213 | }; 214 | 215 | export type TypeAnnotation< 216 | Annotation extends BaseNode, 217 | Data extends NodeData, 218 | > = { 219 | type: 'TypeAnnotation'; 220 | typeAnnotation: Annotation; 221 | data: Data; 222 | }; 223 | 224 | export type StringTypeAnnotation> = { 225 | type: 'StringTypeAnnotation'; 226 | data: Data; 227 | }; 228 | 229 | export type NumberTypeAnnotation> = { 230 | type: 'NumberTypeAnnotation'; 231 | data: Data; 232 | }; 233 | 234 | export type NullLiteralTypeAnnotation> = { 235 | type: 'NullLiteralTypeAnnotation'; 236 | data: Data; 237 | }; 238 | 239 | export type BooleanTypeAnnotation> = { 240 | type: 'BooleanTypeAnnotation'; 241 | data: Data; 242 | }; 243 | 244 | export type GenericTypeAnnotation> = { 245 | type: 'GenericTypeAnnotation'; 246 | id: I; 247 | data: Data; 248 | }; 249 | 250 | export type AnyTypeAnnotation> = { 251 | type: 'AnyTypeAnnotation'; 252 | data: Data; 253 | }; 254 | 255 | export type BaseNode> = 256 | | NumericLiteral 257 | | BooleanLiteral 258 | | StringLiteral 259 | | ArrayExpression 260 | | ObjectExpression 261 | | ObjectProperty 262 | | VariableDeclaration 263 | | VariableDeclarator 264 | | FunctionDeclaration 265 | | FunctionExpression 266 | | Identifier 267 | | NullLiteral 268 | | ExpressionStatement 269 | | CallExpression 270 | | MemberExpression 271 | | IfStatement 272 | | ReturnStatement 273 | | AssignmentExpression 274 | | BinaryExpression 275 | | BlockStatement 276 | | TypeAnnotation 277 | | StringTypeAnnotation 278 | | NumberTypeAnnotation 279 | | NullLiteralTypeAnnotation 280 | | BooleanTypeAnnotation 281 | | GenericTypeAnnotation 282 | | AnyTypeAnnotation; 283 | -------------------------------------------------------------------------------- /src/Parser/Parser.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ArrayExpression, 3 | BooleanLiteral, 4 | NumericLiteral, 5 | ObjectExpression, 6 | StringLiteral, 7 | ObjectProperty, 8 | VariableDeclaration, 9 | VariableDeclarator, 10 | FunctionDeclaration, 11 | FunctionExpression, 12 | Identifier, 13 | NullLiteral, 14 | ExpressionStatement, 15 | CallExpression, 16 | MemberExpression, 17 | IfStatement, 18 | ReturnStatement, 19 | AssignmentExpression, 20 | BlockStatement, 21 | TypeAnnotation, 22 | GenericTypeAnnotation, 23 | StringTypeAnnotation, 24 | BooleanTypeAnnotation, 25 | NullLiteralTypeAnnotation, 26 | NumberTypeAnnotation, 27 | AnyTypeAnnotation, 28 | NodeData, 29 | BaseNode, 30 | ParseArrayResult, 31 | ParseError, 32 | ParseErrorResult, 33 | ParseResult, 34 | ScopeType, 35 | NodeRequiresSemicolon, 36 | BinaryExpression, 37 | } from '.'; 38 | import type { 39 | GenericToken, 40 | NumberToken, 41 | StringToken, 42 | SymbolToken, 43 | KeywordToken, 44 | Token, 45 | TokenData, 46 | } from '../Tokenizer'; 47 | import type { Push, Tail, TailBy, ObjectMerge, ParsingError } from '../Utils'; 48 | 49 | type ParseIdentifier< 50 | TokenList extends Array>, 51 | CanBeAnnotated extends boolean, 52 | > = TokenList[0] extends SymbolToken< 53 | infer Name, 54 | TokenData 55 | > 56 | ? CanBeAnnotated extends true 57 | ? TokenList[1] extends GenericToken< 58 | ':', 59 | TokenData 60 | > 61 | ? ParseTypeAnnotation> extends ParseResult< 62 | infer Node, 63 | infer TokenList, 64 | infer Error 65 | > 66 | ? Error extends ParsingError 67 | ? ParseError 68 | : ParseResult< 69 | Identifier< 70 | Name, 71 | Node, 72 | NodeData 73 | >, 74 | TokenList 75 | > 76 | : ParseErrorResult<'Type expected.', ColonLineNumber> 77 | : ParseResult< 78 | Identifier< 79 | Name, 80 | null, 81 | NodeData 82 | >, 83 | Tail 84 | > 85 | : ParseResult< 86 | Identifier< 87 | Name, 88 | null, 89 | NodeData 90 | >, 91 | Tail 92 | > 93 | : null; 94 | 95 | type ParseTypeAnnotation>> = 96 | TokenList[0] extends SymbolToken<'string', TokenData> 97 | ? ParseResult< 98 | TypeAnnotation< 99 | StringTypeAnnotation>, 100 | NodeData 101 | >, 102 | Tail 103 | > 104 | : TokenList[0] extends SymbolToken< 105 | 'boolean', 106 | TokenData 107 | > 108 | ? ParseResult< 109 | TypeAnnotation< 110 | BooleanTypeAnnotation>, 111 | NodeData 112 | >, 113 | Tail 114 | > 115 | : TokenList[0] extends KeywordToken< 116 | 'null', 117 | TokenData 118 | > 119 | ? ParseResult< 120 | TypeAnnotation< 121 | NullLiteralTypeAnnotation>, 122 | NodeData 123 | >, 124 | Tail 125 | > 126 | : TokenList[0] extends SymbolToken< 127 | 'number', 128 | TokenData 129 | > 130 | ? ParseResult< 131 | TypeAnnotation< 132 | NumberTypeAnnotation>, 133 | NodeData 134 | >, 135 | Tail 136 | > 137 | : TokenList[0] extends SymbolToken<'any', TokenData> 138 | ? ParseResult< 139 | TypeAnnotation< 140 | AnyTypeAnnotation>, 141 | NodeData 142 | >, 143 | Tail 144 | > 145 | : TokenList[0] extends SymbolToken< 146 | infer E, 147 | TokenData 148 | > 149 | ? ParseResult< 150 | TypeAnnotation< 151 | GenericTypeAnnotation>, 152 | NodeData 153 | >, 154 | Tail 155 | > 156 | : null; 157 | 158 | type ParseConstVariableDeclaration< 159 | TokenList extends Array>, 160 | Scope extends ScopeType, 161 | Id extends BaseNode>, 162 | KindLineNumber extends number, 163 | > = TokenList[0] extends GenericToken< 164 | '=', 165 | TokenData 166 | > 167 | ? ParseExpression> extends ParseResult< 168 | infer Node, 169 | infer TokenList, 170 | infer Error 171 | > 172 | ? Error extends ParsingError 173 | ? ParseError 174 | : Id extends Identifier> 175 | ? ParseVariableDeclarationHelper< 176 | TokenList, 177 | Name, 178 | Scope, 179 | Id, 180 | Node, 181 | KindLineNumber, 182 | 'const' 183 | > 184 | : never 185 | : ParseErrorResult<'Expression expected.', EqualsLineNumber> 186 | : ParseError< 187 | ParsingError< 188 | "'const' declarations must be initialized.", 189 | Id['data']['startLineNumber'] 190 | > 191 | >; 192 | 193 | type ParseLetVariableDeclaration< 194 | TokenList extends Array>, 195 | Scope extends ScopeType, 196 | Id extends BaseNode>, 197 | KindLineNumber extends number, 198 | > = Id extends Identifier> 199 | ? TokenList[0] extends GenericToken< 200 | '=', 201 | TokenData 202 | > 203 | ? ParseExpression> extends ParseResult< 204 | infer Node, 205 | infer TokenList, 206 | infer Error 207 | > 208 | ? Error extends ParsingError 209 | ? ParseError 210 | : ParseVariableDeclarationHelper< 211 | TokenList, 212 | Name, 213 | Scope, 214 | Id, 215 | Node, 216 | KindLineNumber, 217 | 'let' 218 | > 219 | : ParseErrorResult<'Expression expected.', EqualsLineNumber> 220 | : ParseError< 221 | ParsingError< 222 | "'let' declarations must be initialized.", 223 | Id['data']['startLineNumber'] 224 | > 225 | > 226 | : never; 227 | 228 | type ParseVariableDeclarationHelper< 229 | TokenList extends Array>, 230 | Name extends string, 231 | Scope extends ScopeType, 232 | Id extends BaseNode>, 233 | Init extends BaseNode>, 234 | KindLineNumber extends number, 235 | Kind extends string, 236 | > = Name extends keyof Scope 237 | ? ParseError< 238 | ParsingError< 239 | `Cannot redeclare block-scoped variable '${Name}'.`, 240 | KindLineNumber 241 | > 242 | > 243 | : ParseResult< 244 | VariableDeclaration< 245 | [ 246 | VariableDeclarator< 247 | Id, 248 | Init, 249 | NodeData< 250 | Id['data']['startLineNumber'], 251 | Init extends BaseNode 252 | ? Init['data']['endLineNumber'] 253 | : Id['data']['endLineNumber'] 254 | > 255 | >, 256 | ], 257 | Kind, 258 | NodeData< 259 | KindLineNumber, 260 | Init extends BaseNode 261 | ? Init['data']['endLineNumber'] 262 | : Id['data']['endLineNumber'] 263 | > 264 | >, 265 | TokenList, 266 | null, 267 | ObjectMerge 268 | >; 269 | 270 | type ParseVariableDeclaration< 271 | TokenList extends Array>, 272 | Scope extends ScopeType, 273 | > = TokenList[0] extends KeywordToken< 274 | infer Kind, 275 | TokenData 276 | > 277 | ? Kind extends 'const' | 'let' 278 | ? ParseIdentifier, true> extends ParseResult< 279 | infer Node, 280 | infer TokenList, 281 | infer Error 282 | > 283 | ? Error extends ParsingError 284 | ? ParseError 285 | : Kind extends 'const' 286 | ? ParseConstVariableDeclaration 287 | : ParseLetVariableDeclaration 288 | : TokenList[1] extends KeywordToken 289 | ? ParseError< 290 | ParsingError<`Unexpected reserved word '${Value}'.`, KindLineNumber> 291 | > 292 | : ParseError< 293 | ParsingError< 294 | 'Variable declaration list cannot be empty.', 295 | KindLineNumber 296 | > 297 | > 298 | : null 299 | : null; 300 | 301 | type ParseMemberExpression< 302 | Node extends BaseNode>, 303 | TokenList extends Array>, 304 | > = TokenList[0] extends GenericToken<'.', TokenData> 305 | ? TokenList[1] extends SymbolToken< 306 | infer Name, 307 | TokenData 308 | > 309 | ? ParseResult< 310 | MemberExpression< 311 | Node, 312 | Identifier< 313 | Name, 314 | null, 315 | NodeData 316 | >, 317 | false, 318 | NodeData 319 | >, 320 | TailBy 321 | > 322 | : ParseErrorResult<'Identifier expected.', DotLineNumber> 323 | : TokenList[0] extends GenericToken< 324 | '[', 325 | TokenData 326 | > 327 | ? ParseExpression> extends ParseResult< 328 | infer ExpressionNode, 329 | infer ExpressionTokenList, 330 | infer ExpressionError 331 | > 332 | ? ExpressionError extends ParsingError 333 | ? ParseError 334 | : ExpressionTokenList[0] extends GenericToken< 335 | ']', 336 | TokenData 337 | > 338 | ? ParseResult< 339 | MemberExpression< 340 | Node, 341 | ExpressionNode, 342 | true, 343 | NodeData< 344 | ExpressionNode['data']['startLineNumber'], 345 | ClosingBracketLineNumber 346 | > 347 | >, 348 | Tail 349 | > 350 | : ParseError< 351 | ParsingError< 352 | "']' expected.", 353 | ExpressionNode['data']['startLineNumber'] 354 | > 355 | > 356 | : ParseErrorResult<'Expression expected.', BracketLineNumber> 357 | : null; 358 | 359 | type ParseAssignmentExpression< 360 | Left extends BaseNode>, 361 | TokenList extends Array>, 362 | > = TokenList[0] extends GenericToken< 363 | '=', 364 | TokenData 365 | > 366 | ? ParseExpression> extends ParseResult< 367 | infer RightNode, 368 | infer RightTokenList, 369 | infer RightError 370 | > 371 | ? RightError extends ParsingError 372 | ? ParseError 373 | : Left extends 374 | | Identifier> 375 | | MemberExpression> 376 | ? ParseResult< 377 | AssignmentExpression< 378 | Left, 379 | RightNode, 380 | '=', 381 | NodeData 382 | >, 383 | RightTokenList 384 | > 385 | : ParseErrorResult< 386 | 'The left-hand side of an assignment expression must be a variable or a property access.', 387 | EqualsLineNumber 388 | > 389 | : ParseErrorResult<'Expression expected.', EqualsLineNumber> 390 | : null; 391 | 392 | type ParseCallExpression< 393 | Node extends BaseNode>, 394 | TokenList extends Array>, 395 | > = TokenList[0] extends GenericToken< 396 | '(', 397 | TokenData 398 | > 399 | ? ParseCallExpressionArguments< 400 | Tail, 401 | ParenLineNumber, 402 | ')' 403 | > extends ParseArrayResult 404 | ? Error extends ParsingError 405 | ? ParseError 406 | : ParseResult< 407 | CallExpression< 408 | Node, 409 | NodeList, 410 | NodeData< 411 | Node['data']['startLineNumber'], 412 | TokenList[0]['data']['lineNumber'] 413 | > 414 | >, 415 | Tail 416 | > 417 | : null 418 | : null; 419 | 420 | type ParseCallExpressionArguments< 421 | TokenList extends Array>, 422 | ParenLineNumber extends number, 423 | ClosingString extends string, 424 | NeedComma extends boolean = false, 425 | Result extends Array> = [], 426 | > = TokenList[0] extends GenericToken 427 | ? ParseArrayResult 428 | : TokenList extends [] 429 | ? ParseErrorResult<`'${ClosingString}' expected.`, ParenLineNumber> 430 | : NeedComma extends true 431 | ? TokenList[0] extends GenericToken<',', any> 432 | ? ParseCallExpressionArgumentsHelper< 433 | Tail, 434 | ParenLineNumber, 435 | ClosingString, 436 | Result 437 | > 438 | : TokenList[0] extends Token> 439 | ? ParseErrorResult<"',' expected.", LineNumber> 440 | : never 441 | : ParseCallExpressionArgumentsHelper< 442 | TokenList, 443 | ParenLineNumber, 444 | ClosingString, 445 | Result 446 | >; 447 | 448 | type ParseCallExpressionArgumentsHelper< 449 | TokenList extends Array>, 450 | ParenLineNumber extends number, 451 | ClosingString extends string, 452 | Result extends Array> = [], 453 | > = ParseExpression extends ParseResult< 454 | infer Node, 455 | infer TokenList, 456 | infer Error 457 | > 458 | ? Error extends ParsingError 459 | ? ParseError 460 | : TokenList[0] extends GenericToken<',', any> 461 | ? ParseCallExpressionArguments< 462 | Tail, 463 | ParenLineNumber, 464 | ClosingString, 465 | false, 466 | Push 467 | > 468 | : ParseCallExpressionArguments< 469 | TokenList, 470 | ParenLineNumber, 471 | ClosingString, 472 | true, 473 | Push 474 | > 475 | : null; 476 | 477 | type ParseBinaryExpression< 478 | LeftNode extends BaseNode>, 479 | TokenList extends Array>>, 480 | > = TokenList[0] extends GenericToken< 481 | '===' | '==' | '+' | '-' | '*' | '/', 482 | TokenData 483 | > 484 | ? ParseExpression> extends ParseResult< 485 | infer RightNode, 486 | infer RightTokenList, 487 | infer RightError 488 | > 489 | ? RightError extends ParsingError 490 | ? ParseError 491 | : ParseResult< 492 | BinaryExpression< 493 | LeftNode, 494 | RightNode, 495 | TokenList[0]['value'], 496 | NodeData< 497 | LeftNode['data']['startLineNumber'], 498 | RightNode['data']['endLineNumber'] 499 | > 500 | >, 501 | RightTokenList 502 | > 503 | : ParseErrorResult< 504 | 'Expression expected.', 505 | TokenList[0]['data']['lineNumber'] 506 | > 507 | : null; 508 | 509 | type CheckExpression< 510 | Node extends BaseNode, 511 | TokenList extends Array>, 512 | > = ParseMemberExpression extends ParseResult< 513 | infer Node, 514 | infer TokenList, 515 | infer Error 516 | > 517 | ? Error extends ParsingError 518 | ? ParseError 519 | : CheckExpression 520 | : ParseAssignmentExpression extends ParseResult< 521 | infer Node, 522 | infer TokenList, 523 | infer Error 524 | > 525 | ? Error extends ParsingError 526 | ? ParseError 527 | : CheckExpression 528 | : ParseBinaryExpression extends ParseResult< 529 | infer Node, 530 | infer TokenList, 531 | infer Error 532 | > 533 | ? Error extends ParsingError 534 | ? ParseError 535 | : CheckExpression 536 | : ParseCallExpression extends ParseResult< 537 | infer Node, 538 | infer TokenList, 539 | infer Error 540 | > 541 | ? Error extends ParsingError 542 | ? ParseError 543 | : CheckExpression 544 | : ParseResult; 545 | 546 | type ParseExpression>> = 547 | ParseExpressionHelper> extends ParseResult< 548 | infer Node, 549 | infer TokenList, 550 | infer Error 551 | > 552 | ? Error extends ParsingError 553 | ? ParseError 554 | : CheckExpression 555 | : null; 556 | 557 | type TokenToNodeData> = InputToken extends Token< 558 | TokenData 559 | > 560 | ? NodeData 561 | : never; 562 | 563 | type ParseExpressionHelper< 564 | TokenList extends Array>, 565 | TokenTail extends Array> = Tail, 566 | Data extends NodeData = TokenToNodeData, 567 | > = TokenList[0] extends KeywordToken<'true', any> 568 | ? ParseResult, TokenTail> 569 | : TokenList[0] extends KeywordToken<'false', any> 570 | ? ParseResult, TokenTail> 571 | : TokenList[0] extends KeywordToken<'null', any> 572 | ? ParseResult, TokenTail> 573 | : TokenList[0] extends NumberToken 574 | ? ParseResult, TokenTail> 575 | : TokenList[0] extends StringToken 576 | ? ParseResult, TokenTail> 577 | : ParseFunctionExpression extends ParseResult< 578 | infer Node, 579 | infer TokenList, 580 | infer Error 581 | > 582 | ? ParseResult 583 | : TokenList[0] extends SymbolToken 584 | ? ParseResult, TokenTail> 585 | : TokenList[0] extends GenericToken<'[', TokenData> 586 | ? ParseArrayExpression, LineNumber> 587 | : TokenList[0] extends GenericToken<'{', TokenData> 588 | ? ParseObject, LineNumber> 589 | : null; 590 | 591 | type ParseObject< 592 | TokenList extends Array>, 593 | InitialLineNumber extends number, 594 | Result extends Array> = [], 595 | NeedComma extends boolean = false, 596 | > = TokenList[0] extends GenericToken<'}', TokenData> 597 | ? ParseResult< 598 | ObjectExpression>, 599 | Tail 600 | > 601 | : TokenList extends [] 602 | ? ParseErrorResult<"'}' expected.", InitialLineNumber> 603 | : NeedComma extends true 604 | ? TokenList[0] extends GenericToken<',', any> 605 | ? ParseObjectItem, InitialLineNumber, Result> 606 | : TokenList[0] extends Token> 607 | ? ParseErrorResult<"',' expected.", L> 608 | : never 609 | : ParseObjectItem; 610 | 611 | type ParseObjectItem< 612 | TokenList extends Array>, 613 | InitialLineNumber extends number, 614 | Result extends Array> = [], 615 | > = TokenList[0] extends SymbolToken< 616 | infer Name, 617 | TokenData 618 | > 619 | ? TokenList[1] extends GenericToken<':', any> 620 | ? ParseExpression> extends ParseResult< 621 | infer ValueNode, 622 | infer TokenList, 623 | infer Error 624 | > 625 | ? Error extends ParsingError 626 | ? ParseError 627 | : ParseObjectItemHelper< 628 | TokenList, 629 | Identifier>, 630 | ValueNode, 631 | InitialLineNumber, 632 | Result, 633 | NameLineNumber 634 | > 635 | : ParseErrorResult<'Expression expected.', InitialLineNumber> 636 | : ParseObjectItemHelper< 637 | Tail, 638 | Identifier>, 639 | Identifier>, 640 | InitialLineNumber, 641 | Result, 642 | NameLineNumber 643 | > 644 | : ParseErrorResult<"'}' expected.", InitialLineNumber>; 645 | 646 | type ParseObjectItemHelper< 647 | TokenList extends Array>, 648 | Key extends BaseNode, 649 | Value extends BaseNode, 650 | InitialLineNumber extends number, 651 | Result extends Array>, 652 | NameLineNumber extends number, 653 | > = TokenList[0] extends GenericToken<',', any> 654 | ? ParseObject< 655 | Tail, 656 | InitialLineNumber, 657 | Push< 658 | Result, 659 | ObjectProperty< 660 | Key, 661 | Value, 662 | NodeData 663 | > 664 | >, 665 | false 666 | > 667 | : ParseObject< 668 | TokenList, 669 | InitialLineNumber, 670 | Push< 671 | Result, 672 | ObjectProperty< 673 | Key, 674 | Value, 675 | NodeData 676 | > 677 | >, 678 | true 679 | >; 680 | 681 | type ParseArrayExpression< 682 | TokenList extends Array>, 683 | StartLineNumber extends number, 684 | > = ParseCallExpressionArguments< 685 | TokenList, 686 | StartLineNumber, 687 | ']' 688 | > extends ParseArrayResult 689 | ? Error extends ParsingError 690 | ? ParseError 691 | : ParseResult< 692 | ArrayExpression< 693 | NodeList, 694 | NodeData 695 | >, 696 | Tail 697 | > 698 | : null; 699 | 700 | type ParseExpressionStatement>> = 701 | ParseExpression extends ParseResult< 702 | infer Node, 703 | infer TokenList, 704 | infer Error 705 | > 706 | ? Error extends ParsingError 707 | ? ParseError 708 | : ParseResult, TokenList> 709 | : null; 710 | 711 | type ParseFunctionDeclaration< 712 | TokenList extends Array>, 713 | Scope extends ScopeType, 714 | > = TokenList[0] extends KeywordToken< 715 | 'function', 716 | TokenData 717 | > 718 | ? TokenList[1] extends SymbolToken< 719 | infer Name, 720 | TokenData 721 | > 722 | ? TokenList[2] extends GenericToken< 723 | '(', 724 | TokenData 725 | > 726 | ? ParseFunctionParams< 727 | TailBy, 728 | ParenLineNumber 729 | > extends ParseArrayResult 730 | ? Error extends ParsingError 731 | ? ParseError 732 | : ParseBlockStatement< 733 | Tail, 734 | {}, 735 | TokenList[0]['data']['lineNumber'], 736 | true 737 | > extends ParseResult 738 | ? Error extends ParsingError 739 | ? ParseError 740 | : Name extends keyof Scope 741 | ? ParseError< 742 | ParsingError< 743 | `Cannot redeclare block-scoped variable '${Name}'.`, 744 | FunctionLineNumber 745 | > 746 | > 747 | : ParseResult< 748 | FunctionDeclaration< 749 | Identifier< 750 | Name, 751 | null, 752 | NodeData 753 | >, 754 | NodeList, 755 | Node, 756 | NodeData 757 | >, 758 | TokenList, 759 | null, 760 | ObjectMerge 761 | > 762 | : never 763 | : never 764 | : ParseErrorResult<"'(' expected.", FunctionNameLineNumber> 765 | : ParseErrorResult<'Identifier expected.', FunctionLineNumber> 766 | : null; 767 | 768 | type ParseFunctionExpression>> = 769 | TokenList[0] extends KeywordToken< 770 | 'function', 771 | TokenData 772 | > 773 | ? TokenList[1] extends SymbolToken< 774 | infer Name, 775 | TokenData 776 | > 777 | ? ParseFunctionExpressionHelper< 778 | TailBy, 779 | Identifier< 780 | Name, 781 | null, 782 | NodeData 783 | >, 784 | FunctionLineNumber 785 | > 786 | : ParseFunctionExpressionHelper, null, FunctionLineNumber> 787 | : null; 788 | 789 | type ParseFunctionExpressionHelper< 790 | TokenList extends Array>, 791 | Id extends BaseNode | null, 792 | FunctionLineNumber extends number, 793 | > = TokenList[0] extends GenericToken< 794 | '(', 795 | TokenData 796 | > 797 | ? ParseFunctionParams< 798 | Tail, 799 | ParenLineNumber 800 | > extends ParseArrayResult 801 | ? Error extends ParsingError 802 | ? ParseError 803 | : ParseBlockStatement< 804 | Tail, 805 | {}, 806 | FunctionLineNumber, 807 | true 808 | > extends ParseResult 809 | ? Error extends ParsingError 810 | ? ParseError 811 | : ParseResult< 812 | FunctionExpression< 813 | Id, 814 | NodeList, 815 | Node, 816 | NodeData 817 | >, 818 | TokenList 819 | > 820 | : never 821 | : never 822 | : ParseErrorResult<"'(' expected.", FunctionLineNumber>; 823 | 824 | type ParseFunctionParams< 825 | TokenList extends Array>, 826 | InitialLineNumber extends number, 827 | Result extends Array>> = [], 828 | NeedSemicolon extends boolean = false, 829 | > = TokenList[0] extends GenericToken< 830 | ')', 831 | TokenData 832 | > 833 | ? TokenList[1] extends GenericToken<'{', any> 834 | ? ParseArrayResult> 835 | : ParseErrorResult<"'{' expected.", ParenLineNumber> 836 | : TokenList extends [] 837 | ? ParseErrorResult<"')' expected.", InitialLineNumber> 838 | : NeedSemicolon extends true 839 | ? TokenList[0] extends GenericToken<',', any> 840 | ? ParseFunctionParamsHelper, InitialLineNumber, Result> 841 | : ParseError< 842 | ParsingError<"',' expected.", Result[0]['data']['endLineNumber']> 843 | > 844 | : ParseFunctionParamsHelper; 845 | 846 | type ParseFunctionParamsHelper< 847 | TokenList extends Array>, 848 | LineNumber extends number, 849 | Result extends Array>, 850 | > = ParseIdentifier extends ParseResult< 851 | infer Node, 852 | infer TokenList, 853 | infer Error 854 | > 855 | ? Error extends ParsingError 856 | ? ParseError 857 | : ParseFunctionParams, true> 858 | : ParseErrorResult<'Identifier expected.', LineNumber>; 859 | 860 | type ParseBlockStatement< 861 | TokenList extends Array>, 862 | Scope extends ScopeType, 863 | InitialLineNumber extends number, 864 | InFunctionScope extends boolean, 865 | Result extends Array> = [], 866 | NeedSemicolon extends boolean = false, 867 | > = TokenList extends [] 868 | ? Result[0] extends BaseNode> 869 | ? ParseErrorResult<"'}' expected.", LineNumber> 870 | : ParseErrorResult<"'}' expected.", InitialLineNumber> 871 | : TokenList[0] extends GenericToken<'}', TokenData> 872 | ? ParseResult< 873 | BlockStatement>, 874 | Tail 875 | > 876 | : TokenList[0] extends GenericToken<';', any> 877 | ? ParseBlockStatement< 878 | Tail, 879 | Scope, 880 | InitialLineNumber, 881 | InFunctionScope, 882 | Result, 883 | false 884 | > 885 | : NeedSemicolon extends false 886 | ? ParseBlockStatementHelper< 887 | TokenList, 888 | InitialLineNumber, 889 | InFunctionScope, 890 | Result, 891 | Scope 892 | > 893 | : TokenList[0] extends Token< 894 | TokenData 895 | > 896 | ? PrecedingLinebreak extends true 897 | ? ParseBlockStatementHelper< 898 | TokenList, 899 | LineNumber, 900 | InFunctionScope, 901 | Result, 902 | Scope 903 | > 904 | : ParseErrorResult<"';' expected.", LineNumber> 905 | : never; 906 | 907 | type ParseTopLevel< 908 | TokenList extends Array>, 909 | Scope extends ScopeType, 910 | Result extends Array> = [], 911 | NeedSemicolon extends boolean = false, 912 | > = TokenList extends [] 913 | ? ParseArrayResult 914 | : TokenList[0] extends GenericToken<';', any> 915 | ? ParseTopLevel, Scope, Result, false> 916 | : NeedSemicolon extends false 917 | ? ParseTopLevelHelper 918 | : TokenList[0] extends Token< 919 | TokenData 920 | > 921 | ? PrecedingLinebreak extends true 922 | ? ParseTopLevelHelper 923 | : ParseErrorResult<"';' expected.", LineNumber> 924 | : never; 925 | 926 | type ParseBlockStatementHelper< 927 | TokenList extends Array>, 928 | LineNumber extends number, 929 | InFunctionScope extends boolean, 930 | Result extends Array>, 931 | Scope extends ScopeType, 932 | > = ParseStatementHelper extends ParseResult< 933 | infer Node, 934 | infer TokenList, 935 | infer Error, 936 | infer Scope 937 | > 938 | ? Error extends ParsingError 939 | ? ParseError 940 | : ParseBlockStatement< 941 | TokenList, 942 | Scope, 943 | LineNumber, 944 | InFunctionScope, 945 | Push, 946 | NodeRequiresSemicolon 947 | > 948 | : never; 949 | 950 | type ParseTopLevelHelper< 951 | TokenList extends Array>, 952 | Result extends Array>, 953 | Scope extends ScopeType, 954 | > = ParseStatementHelper extends ParseResult< 955 | infer Node, 956 | infer TokenList, 957 | infer Error, 958 | infer Scope 959 | > 960 | ? Error extends ParsingError 961 | ? ParseError 962 | : ParseTopLevel< 963 | TokenList, 964 | Scope, 965 | Push, 966 | NodeRequiresSemicolon 967 | > 968 | : never; 969 | 970 | type ParseIfStatement< 971 | TokenList extends Array>, 972 | InFunctionScope extends boolean, 973 | > = TokenList[0] extends KeywordToken<'if', TokenData> 974 | ? TokenList[1] extends GenericToken< 975 | '(', 976 | TokenData 977 | > 978 | ? ParseExpression> extends ParseResult< 979 | infer Node, 980 | infer TokenList, 981 | infer Error 982 | > 983 | ? Error extends ParsingError 984 | ? ParseError 985 | : ParseIfStatementHelper< 986 | Node, 987 | TokenList, 988 | IfLineNumber, 989 | InFunctionScope, 990 | Node['data']['endLineNumber'] 991 | > 992 | : ParseErrorResult<'Expression expected.', ParenLineNumber> 993 | : ParseErrorResult<"'(' expected.", IfLineNumber> 994 | : null; 995 | 996 | type ParseReturnStatementHelper< 997 | TokenList extends Array>, 998 | StartLineNumber extends number, 999 | > = TokenList[0] extends GenericToken< 1000 | ';', 1001 | TokenData 1002 | > 1003 | ? ParseResult< 1004 | ReturnStatement>, 1005 | Tail 1006 | > 1007 | : ParseExpression extends ParseResult< 1008 | infer Node, 1009 | infer TokenList, 1010 | infer Error 1011 | > 1012 | ? Error extends ParsingError 1013 | ? ParseError 1014 | : ParseResult< 1015 | ReturnStatement< 1016 | Node, 1017 | NodeData 1018 | >, 1019 | TokenList 1020 | > 1021 | : ParseErrorResult<'Expression expected.', 1>; 1022 | 1023 | type ParseReturnStatement< 1024 | TokenList extends Array>, 1025 | InFunctionScope extends boolean, 1026 | > = TokenList[0] extends KeywordToken< 1027 | 'return', 1028 | TokenData 1029 | > 1030 | ? InFunctionScope extends true 1031 | ? TokenList[1] extends Token, any> 1032 | ? PrecedingLinebreak extends false 1033 | ? ParseReturnStatementHelper, LineNumber> 1034 | : ParseResult< 1035 | ReturnStatement>, 1036 | Tail 1037 | > 1038 | : ParseResult>, []> 1039 | : ParseError< 1040 | ParsingError< 1041 | "A 'return' statement can only be used within a function body.", 1042 | LineNumber 1043 | > 1044 | > 1045 | : null; 1046 | 1047 | type ParseIfStatementHelper< 1048 | Node extends BaseNode, 1049 | TokenList extends Array>, 1050 | StartLineNumber extends number, 1051 | InFunctionScope extends boolean, 1052 | IfExpressionLineNumber extends number, 1053 | > = TokenList[0] extends GenericToken< 1054 | ')', 1055 | TokenData 1056 | > 1057 | ? TokenList[1] extends GenericToken< 1058 | '{', 1059 | TokenData 1060 | > 1061 | ? ParseBlockStatement< 1062 | TailBy, 1063 | {}, 1064 | CurlyLineNumber, 1065 | InFunctionScope 1066 | > extends ParseResult 1067 | ? Error extends ParsingError 1068 | ? ParseError 1069 | : ParseResult< 1070 | IfStatement< 1071 | Node, 1072 | BlockNode, 1073 | NodeData 1074 | >, 1075 | TokenList 1076 | > 1077 | : never 1078 | : ParseErrorResult<"'{' expected.", ClosingParenLineNumber> 1079 | : ParseErrorResult<"')' expected.", IfExpressionLineNumber>; 1080 | 1081 | type ParseStatementHelper< 1082 | TokenList extends Array>, 1083 | InFunctionScope extends boolean, 1084 | Scope extends ScopeType, 1085 | > = ParseFunctionDeclaration extends ParseResult< 1086 | infer Node, 1087 | infer TokenList, 1088 | infer Error, 1089 | infer Scope 1090 | > 1091 | ? Error extends ParsingError 1092 | ? ParseError 1093 | : ParseResult 1094 | : ParseVariableDeclaration extends ParseResult< 1095 | infer Node, 1096 | infer TokenList, 1097 | infer Error, 1098 | infer Scope 1099 | > 1100 | ? Error extends ParsingError 1101 | ? ParseError 1102 | : ParseResult 1103 | : ParseIfStatement extends ParseResult< 1104 | infer Node, 1105 | infer TokenList, 1106 | infer Error 1107 | > 1108 | ? Error extends ParsingError 1109 | ? ParseError 1110 | : ParseResult 1111 | : ParseReturnStatement extends ParseResult< 1112 | infer Node, 1113 | infer TokenList, 1114 | infer Error 1115 | > 1116 | ? Error extends ParsingError 1117 | ? ParseError 1118 | : ParseResult 1119 | : ParseExpressionStatement extends ParseResult< 1120 | infer Node, 1121 | infer TokenList, 1122 | infer Error 1123 | > 1124 | ? Error extends ParsingError 1125 | ? ParseError 1126 | : ParseResult 1127 | : ParseErrorResult<'Declaration or statement expected.', 1>; 1128 | 1129 | export type Parse>> = ParseTopLevel< 1130 | TokenList, 1131 | {} 1132 | > extends ParseArrayResult 1133 | ? Error extends ParsingError 1134 | ? Error 1135 | : NodeList 1136 | : never; 1137 | -------------------------------------------------------------------------------- /src/Parser/Utils.ts: -------------------------------------------------------------------------------- 1 | import type { BaseNode, NodeData, IfStatement, FunctionDeclaration } from '.'; 2 | import type { ParsingError } from '../Utils'; 3 | import type { Token, TokenData } from '../Tokenizer'; 4 | 5 | export type ParseResult< 6 | Node extends BaseNode>, 7 | TokenList extends Array>, 8 | Error extends ParsingError | null = null, 9 | Scope extends ScopeType = {}, 10 | > = { 11 | type: 'ParseResult'; 12 | node: Node; 13 | tokenList: TokenList; 14 | error: Error; 15 | scope: Scope; 16 | }; 17 | 18 | export type ParseArrayResult< 19 | NodeList extends Array>>, 20 | TokenList extends Array>>, 21 | Error extends ParsingError | null = null, 22 | Scope extends ScopeType = {}, 23 | > = { 24 | type: 'ParseResult'; 25 | node: NodeList; 26 | tokenList: TokenList; 27 | error: Error; 28 | scope: Scope; 29 | }; 30 | 31 | export type ParseError> = ParseResult< 32 | any, 33 | any, 34 | Error 35 | >; 36 | 37 | export type ParseErrorResult< 38 | Message extends string, 39 | LineNumber extends number, 40 | > = ParseError>; 41 | 42 | export type ScopeType = Record; 43 | 44 | export type NodeRequiresSemicolon> = 45 | Node extends IfStatement 46 | ? false 47 | : Node extends FunctionDeclaration 48 | ? false 49 | : true; 50 | -------------------------------------------------------------------------------- /src/Parser/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Parser'; 2 | export * from './Ast'; 3 | export * from './Utils'; 4 | -------------------------------------------------------------------------------- /src/Serializer/Serializer.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | AnyType, 3 | ArrayType, 4 | BooleanType, 5 | FunctionType, 6 | NeverType, 7 | NullType, 8 | NumberType, 9 | ObjectType, 10 | StaticType, 11 | StringType, 12 | UndefinedType, 13 | UnionType, 14 | UnknownType, 15 | VoidType, 16 | MapLiteralToType, 17 | StringLiteralType, 18 | BooleanLiteralType, 19 | NumberLiteralType, 20 | } from '../Checker'; 21 | import type { Tail } from '../Utils'; 22 | 23 | type SerializeHelper< 24 | Type extends StaticType, 25 | ShouldMapLiterals extends boolean, 26 | > = ShouldMapLiterals extends true ? Type : MapLiteralToType; 27 | 28 | export type Serialize< 29 | Type extends StaticType, 30 | ShouldMapLiterals extends boolean = false, 31 | > = SerializeHelper extends infer MappedType 32 | ? MappedType extends StringType 33 | ? 'string' 34 | : MappedType extends StringLiteralType 35 | ? Value 36 | : MappedType extends BooleanType 37 | ? 'boolean' 38 | : MappedType extends BooleanLiteralType 39 | ? Value 40 | : MappedType extends NumberType 41 | ? 'number' 42 | : MappedType extends NumberLiteralType 43 | ? Value 44 | : MappedType extends NullType 45 | ? 'null' 46 | : MappedType extends UndefinedType 47 | ? 'undefined' 48 | : MappedType extends VoidType 49 | ? 'void' 50 | : MappedType extends AnyType 51 | ? 'any' 52 | : MappedType extends UnknownType 53 | ? 'unknown' 54 | : MappedType extends NeverType 55 | ? 'never' 56 | : MappedType extends ArrayType 57 | ? SerializeArray 58 | : MappedType extends UnionType 59 | ? SerializeUnion 60 | : MappedType extends ObjectType 61 | ? SerializeObject 62 | : MappedType extends FunctionType 63 | ? SerializeFunction 64 | : never 65 | : never; 66 | 67 | type SerializeFunction< 68 | Params extends Array<[string, StaticType]>, 69 | Return extends StaticType, 70 | > = SerializeFunctionParams extends infer SerializedParams 71 | ? SerializedParams extends string 72 | ? `(${SerializedParams}) => ${Serialize}` 73 | : never 74 | : never; 75 | 76 | type SerializeFunctionParams< 77 | Params extends Array<[string, StaticType]>, 78 | Result extends string = '', 79 | > = Params extends [] 80 | ? Result 81 | : Params[0] extends [infer Key, infer Value] 82 | ? Value extends StaticType 83 | ? Key extends string 84 | ? SerializeFunctionParams< 85 | Tail, 86 | `${Result}${Key}: ${Serialize}` extends infer SerializedSignature 87 | ? SerializedSignature extends string 88 | ? Params['length'] extends 1 89 | ? `${SerializedSignature}` 90 | : `${SerializedSignature}, ` 91 | : never 92 | : never 93 | > 94 | : never 95 | : never 96 | : never; 97 | 98 | type ShouldUseParens = Type extends UnionType 99 | ? true 100 | : Type extends FunctionType 101 | ? true 102 | : false; 103 | 104 | type SerializeArray = 105 | Serialize extends infer SerializedElements 106 | ? SerializedElements extends string 107 | ? ShouldUseParens extends true 108 | ? `(${SerializedElements})[]` 109 | : `${SerializedElements}[]` 110 | : never 111 | : never; 112 | 113 | type SerializeUnion< 114 | UnionTypes extends Array, 115 | Result extends string = '', 116 | > = UnionTypes extends [] 117 | ? Result 118 | : UnionTypes[0] extends StaticType 119 | ? Serialize extends infer SerializedType 120 | ? SerializedType extends string 121 | ? SerializeUnion< 122 | Tail, 123 | UnionTypes['length'] extends 1 124 | ? `${Result}${SerializedType}` 125 | : `${Result}${SerializedType} | ` 126 | > 127 | : never 128 | : never 129 | : never; 130 | 131 | type SerializeObject< 132 | Properties extends Array<[string, StaticType]>, 133 | Result extends string = '', 134 | > = Properties extends [] 135 | ? Result extends '' 136 | ? '{}' 137 | : `{ ${Result} }` 138 | : Properties[0] extends [infer Key, infer Value] 139 | ? Value extends StaticType 140 | ? Key extends string 141 | ? `${Key}: ${Serialize}` extends infer SerializedProperty 142 | ? SerializedProperty extends string 143 | ? SerializeObject< 144 | Tail, 145 | Properties['length'] extends 1 146 | ? `${Result}${SerializedProperty};` 147 | : `${Result}${SerializedProperty}; ` 148 | > 149 | : never 150 | : never 151 | : never 152 | : never 153 | : never; 154 | -------------------------------------------------------------------------------- /src/Serializer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Serializer'; 2 | -------------------------------------------------------------------------------- /src/Tokenizer/Inputs.ts: -------------------------------------------------------------------------------- 1 | export type Keywords = 2 | | 'const' 3 | | 'let' 4 | | 'if' 5 | | 'return' 6 | | 'null' 7 | | 'true' 8 | | 'false' 9 | | 'function'; 10 | 11 | export type Punctuation = 12 | | ',' 13 | | '(' 14 | | ')' 15 | | '[' 16 | | ']' 17 | | '{' 18 | | '}' 19 | | '.' 20 | | ';' 21 | | ':' 22 | | '=' 23 | | '*' 24 | | '+' 25 | | '-' 26 | | '/'; 27 | 28 | export type Numbers = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'; 29 | 30 | export type Symbols = 31 | | 'a' 32 | | 'b' 33 | | 'c' 34 | | 'd' 35 | | 'e' 36 | | 'f' 37 | | 'g' 38 | | 'h' 39 | | 'i' 40 | | 'j' 41 | | 'k' 42 | | 'l' 43 | | 'm' 44 | | 'n' 45 | | 'o' 46 | | 'p' 47 | | 'q' 48 | | 'r' 49 | | 's' 50 | | 't' 51 | | 'u' 52 | | 'v' 53 | | 'w' 54 | | 'x' 55 | | 'y' 56 | | 'z' 57 | | 'A' 58 | | 'B' 59 | | 'C' 60 | | 'D' 61 | | 'E' 62 | | 'F' 63 | | 'G' 64 | | 'H' 65 | | 'I' 66 | | 'J' 67 | | 'K' 68 | | 'L' 69 | | 'M' 70 | | 'N' 71 | | 'O' 72 | | 'P' 73 | | 'Q' 74 | | 'R' 75 | | 'S' 76 | | 'T' 77 | | 'U' 78 | | 'V' 79 | | 'W' 80 | | 'X' 81 | | 'Y' 82 | | 'Z' 83 | | '0' 84 | | '1' 85 | | '2' 86 | | '3' 87 | | '4' 88 | | '5' 89 | | '6' 90 | | '7' 91 | | '8' 92 | | '9' 93 | | '_' 94 | | '$'; 95 | -------------------------------------------------------------------------------- /src/Tokenizer/Math.ts: -------------------------------------------------------------------------------- 1 | type SuccTable = { 2 | 0: 1; 3 | 1: 2; 4 | 2: 3; 5 | 3: 4; 6 | 4: 5; 7 | 5: 6; 8 | 6: 7; 9 | 7: 8; 10 | 8: 9; 11 | 9: 10; 12 | 10: 11; 13 | 11: 12; 14 | 12: 13; 15 | 13: 14; 16 | 14: 15; 17 | 15: 16; 18 | 16: 17; 19 | 17: 18; 20 | 18: 19; 21 | 19: 20; 22 | 20: 21; 23 | 21: 22; 24 | 22: 23; 25 | 23: 24; 26 | 24: 25; 27 | 25: 26; 28 | 26: 27; 29 | 27: 28; 30 | 28: 29; 31 | 29: 30; 32 | 30: 31; 33 | 31: 32; 34 | 32: 33; 35 | 33: 34; 36 | 34: 35; 37 | 35: 36; 38 | 36: 37; 39 | 37: 38; 40 | 38: 39; 41 | 39: 40; 42 | 40: 41; 43 | 41: 42; 44 | 42: 43; 45 | 43: 44; 46 | 44: 45; 47 | 45: 46; 48 | 46: 47; 49 | 47: 48; 50 | 48: 49; 51 | 49: 50; 52 | 50: 51; 53 | 51: 52; 54 | 52: 53; 55 | 53: 54; 56 | 54: 55; 57 | 55: 56; 58 | 56: 57; 59 | 57: 58; 60 | 58: 59; 61 | 59: 60; 62 | }; 63 | 64 | export type Succ = T extends keyof SuccTable ? SuccTable[T] : never; 65 | -------------------------------------------------------------------------------- /src/Tokenizer/StringUtils.ts: -------------------------------------------------------------------------------- 1 | export type EatFirstChar = T extends `${infer A}${infer B}` ? B : ''; 2 | 3 | export type GetFirstChar = T extends `${infer A}${infer B}` ? A : ''; 4 | 5 | export type ConcatStrings = `${A}${B}`; 6 | 7 | export type StringContains = I extends T 8 | ? true 9 | : I extends `${T}${infer _}` 10 | ? true 11 | : I extends `${infer _0}${T}${infer _1}` 12 | ? true 13 | : I extends `${infer _}${T}` 14 | ? true 15 | : false; 16 | -------------------------------------------------------------------------------- /src/Tokenizer/Tokenizer.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Token, 3 | NumberToken, 4 | SymbolToken, 5 | StringToken, 6 | TokenData, 7 | GenericToken, 8 | KeywordToken, 9 | Succ, 10 | Punctuation, 11 | Numbers, 12 | Symbols, 13 | Keywords, 14 | EatFirstChar, 15 | GetFirstChar, 16 | ConcatStrings, 17 | StringContains, 18 | } from '.'; 19 | import type { Push, SyntaxError } from '../Utils'; 20 | 21 | type TokenizeInput< 22 | Input extends string, 23 | FirstChar extends string, 24 | InputTail extends string, 25 | PrecedingLinebreak extends boolean, 26 | LineNumber extends number, 27 | Data extends TokenData = TokenData, 28 | > = Input extends `===${infer InputTail}` 29 | ? [GenericToken<'===', Data>, InputTail] 30 | : Input extends `==${infer InputTail}` 31 | ? [GenericToken<'==', Data>, InputTail] 32 | : FirstChar extends Punctuation 33 | ? [GenericToken, InputTail] 34 | : FirstChar extends Numbers 35 | ? TokenizeNumber 36 | : FirstChar extends '"' 37 | ? TokenizeString 38 | : FirstChar extends "'" 39 | ? TokenizeString 40 | : FirstChar extends Symbols 41 | ? TokenizeSymbol 42 | : SyntaxError<`Invalid character.`, LineNumber>; 43 | 44 | type TokenizeNumber< 45 | Input extends string, 46 | Result extends string, 47 | PrecedingLinebreak extends TokenData, 48 | FirstChar extends string = GetFirstChar, 49 | > = FirstChar extends Numbers 50 | ? TokenizeNumber< 51 | EatFirstChar, 52 | ConcatStrings, 53 | PrecedingLinebreak 54 | > 55 | : [NumberToken, Input]; 56 | 57 | type TokenizeString< 58 | Input extends string, 59 | QuoteType extends '"' | "'", 60 | Data extends TokenData, 61 | > = Input extends `${infer Before}${QuoteType}${infer After}` 62 | ? StringContains extends true 63 | ? SyntaxError<'Unterminated string literal.', Data['lineNumber']> 64 | : [StringToken, After] 65 | : SyntaxError<'Unterminated string literal.', Data['lineNumber']>; 66 | 67 | type TokenizeSymbol< 68 | Input extends string, 69 | Result extends string, 70 | PrecedingLinebreak extends TokenData, 71 | FirstChar extends string = GetFirstChar, 72 | > = FirstChar extends Symbols 73 | ? TokenizeSymbol< 74 | EatFirstChar, 75 | ConcatStrings, 76 | PrecedingLinebreak 77 | > 78 | : [ 79 | Result extends Keywords 80 | ? KeywordToken 81 | : SymbolToken, 82 | Input, 83 | ]; 84 | 85 | type TokenizeHelper< 86 | TokenizeResult, 87 | Result extends Array, 88 | LineNumber extends number, 89 | > = TokenizeResult extends Array 90 | ? Tokenize< 91 | TokenizeResult[1], 92 | Push, 93 | LineNumber, 94 | false 95 | > 96 | : TokenizeResult; 97 | 98 | export type Tokenize< 99 | Input extends string, 100 | Result extends Array> = [], 101 | LineNumber extends number = 1, 102 | PrecedingLinebreak extends boolean = false, 103 | FirstChar extends string = GetFirstChar, 104 | InputTail extends string = EatFirstChar, 105 | > = Input extends '' 106 | ? Result 107 | : FirstChar extends ' ' 108 | ? Tokenize 109 | : Input extends `//${infer Commented}\n${infer Rest}` 110 | ? Tokenize, true> 111 | : FirstChar extends '\n' 112 | ? Tokenize, true> 113 | : TokenizeInput< 114 | Input, 115 | FirstChar, 116 | InputTail, 117 | PrecedingLinebreak, 118 | LineNumber 119 | > extends infer TokenizeResult 120 | ? TokenizeHelper 121 | : never; 122 | -------------------------------------------------------------------------------- /src/Tokenizer/Tokens.ts: -------------------------------------------------------------------------------- 1 | export type TokenData< 2 | PrecedingLinebreak extends boolean, 3 | LineNumber extends number, 4 | > = { 5 | precedingLinebreak: PrecedingLinebreak; 6 | lineNumber: LineNumber; 7 | }; 8 | 9 | export type GenericToken< 10 | Value extends string, 11 | Data extends TokenData, 12 | > = { 13 | type: 'generic'; 14 | value: Value; 15 | data: Data; 16 | }; 17 | 18 | export type KeywordToken< 19 | Value extends string, 20 | Data extends TokenData, 21 | > = { 22 | type: 'keyword'; 23 | value: Value; 24 | data: Data; 25 | }; 26 | 27 | export type NumberToken< 28 | Value extends string, 29 | Data extends TokenData, 30 | > = { 31 | type: 'number'; 32 | value: Value; 33 | data: Data; 34 | }; 35 | 36 | export type StringToken< 37 | Value extends string, 38 | Data extends TokenData, 39 | > = { 40 | type: 'string'; 41 | value: Value; 42 | data: Data; 43 | }; 44 | 45 | export type SymbolToken< 46 | Value extends string, 47 | Data extends TokenData, 48 | > = { 49 | type: 'symbol'; 50 | value: Value; 51 | data: Data; 52 | }; 53 | 54 | export type Token< 55 | Data extends TokenData, 56 | Value extends string = string, 57 | > = 58 | | GenericToken 59 | | NumberToken 60 | | StringToken 61 | | SymbolToken 62 | | KeywordToken; 63 | -------------------------------------------------------------------------------- /src/Tokenizer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Tokenizer'; 2 | export * from './Tokens'; 3 | export * from './Math'; 4 | export * from './Inputs'; 5 | export * from './StringUtils'; 6 | -------------------------------------------------------------------------------- /src/Utils/ArrayUtils.ts: -------------------------------------------------------------------------------- 1 | export type Tail> = ((...t: T) => void) extends ( 2 | h: any, 3 | ...rest: infer R 4 | ) => void 5 | ? R 6 | : never; 7 | 8 | export type Push, E> = [...T, E]; 9 | 10 | export type Unshift, E> = [E, ...T]; 11 | 12 | export type Concat, T2 extends Array> = [ 13 | ...T1, 14 | ...T2, 15 | ]; 16 | 17 | export type TailBy< 18 | T extends Array, 19 | B extends number, 20 | A extends Array = [], 21 | > = B extends A['length'] ? T : TailBy, B, Push>; 22 | -------------------------------------------------------------------------------- /src/Utils/Errors.ts: -------------------------------------------------------------------------------- 1 | export type Error = 2 | | SyntaxError 3 | | ParsingError 4 | | TypeError; 5 | 6 | export type SyntaxError = { 7 | type: 'SyntaxError'; 8 | message: Message; 9 | lineNumber: LineNumber; 10 | }; 11 | 12 | export type ParsingError = { 13 | type: 'ParsingError'; 14 | message: Message; 15 | lineNumber: LineNumber; 16 | }; 17 | 18 | export type TypeError = { 19 | type: 'TypeError'; 20 | message: Message; 21 | lineNumber: LineNumber; 22 | }; 23 | -------------------------------------------------------------------------------- /src/Utils/Format.ts: -------------------------------------------------------------------------------- 1 | import type { Push, Tail, Error } from '.'; 2 | 3 | export type Format< 4 | Errors extends Array>, 5 | Result extends Array = [], 6 | > = Errors extends [] 7 | ? Result 8 | : Errors[0] extends Error 9 | ? Format, Push> 10 | : never; 11 | -------------------------------------------------------------------------------- /src/Utils/ObjectUtils.ts: -------------------------------------------------------------------------------- 1 | export type ObjectMerge = Omit & T2; 2 | -------------------------------------------------------------------------------- /src/Utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ArrayUtils'; 2 | export * from './ObjectUtils'; 3 | export * from './Format'; 4 | export * from './Errors'; 5 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Tokenize, Token } from './Tokenizer'; 2 | import type { Parse, BaseNode } from './Parser'; 3 | import type { Check } from './Checker'; 4 | import type { Format, Error } from './Utils'; 5 | 6 | export type TypeCheck = 7 | // Tokenize input 8 | Tokenize extends infer TokenList 9 | ? TokenList extends Error 10 | ? // If any errors, format them 11 | Format<[TokenList]> 12 | : TokenList extends Array> 13 | ? // If successful, parse into an ast 14 | Parse extends infer NodeList 15 | ? NodeList extends Error 16 | ? // If any errors, format them 17 | Format<[NodeList]> 18 | : NodeList extends Array> 19 | ? // If successful, type-check and format errors 20 | Format> 21 | : never 22 | : never 23 | : never 24 | : never; 25 | -------------------------------------------------------------------------------- /test/Arrays.test.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | import { expectType } from './TestUtils'; 3 | 4 | expectType< 5 | TypeCheck<` 6 | 7 | let a = [1, 2, 3]; 8 | 9 | `> 10 | >([]); 11 | 12 | expectType< 13 | TypeCheck<` 14 | 15 | let a = [1, 2, 3]; 16 | 17 | let b: string = a[0]; 18 | 19 | `> 20 | >(["5: Type 'number' is not assignable to type 'string'."]); 21 | 22 | expectType< 23 | TypeCheck<` 24 | 25 | let a: number = [1, '2', 3]; 26 | 27 | `> 28 | >(["3: Type '(number | string)[]' is not assignable to type 'number'."]); 29 | 30 | expectType< 31 | TypeCheck<` 32 | 33 | let a: number = [1, '2', 3][0]; 34 | 35 | `> 36 | >(["3: Type 'number | string' is not assignable to type 'number'."]); 37 | 38 | expectType< 39 | TypeCheck<` 40 | 41 | const a = [1, '2', 3]; 42 | 43 | a[0] = 1; 44 | 45 | a[0] = '2'; 46 | 47 | a[0] = true; 48 | 49 | `> 50 | >(["9: Type 'boolean' is not assignable to type 'number | string'."]); 51 | 52 | expectType< 53 | TypeCheck<` 54 | 55 | const a = [1, '2', 3]; 56 | const b = [true, 1]; 57 | 58 | a[0] = b[0]; 59 | 60 | `> 61 | >(["6: Type 'boolean | number' is not assignable to type 'number | string'."]); 62 | -------------------------------------------------------------------------------- /test/Comparisons.test.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | import { expectType } from './TestUtils'; 3 | 4 | expectType< 5 | TypeCheck<` 6 | 7 | const a: boolean = 1 == 2; 8 | 9 | `> 10 | >([ 11 | "3: This condition will always return 'false' since the types '1' and '2' have no overlap.", 12 | ]); 13 | 14 | expectType< 15 | TypeCheck<` 16 | 17 | const a: boolean = '1' == 2; 18 | 19 | `> 20 | >([ 21 | "3: This condition will always return 'false' since the types 'string' and 'number' have no overlap.", 22 | ]); 23 | 24 | expectType< 25 | TypeCheck<` 26 | 27 | let a = '1'; 28 | 29 | let b = '2'; 30 | 31 | const c: boolean = a === b; 32 | 33 | `> 34 | >([]); 35 | 36 | expectType< 37 | TypeCheck<` 38 | 39 | let a = [1, 2]; 40 | 41 | let b = [1]; 42 | 43 | const c: boolean = a[0] === b[0]; 44 | 45 | `> 46 | >([]); 47 | 48 | expectType< 49 | TypeCheck<` 50 | 51 | let a = [1, '2']; 52 | 53 | let b = [1]; 54 | 55 | const c: boolean = a[0] === b[0]; 56 | 57 | `> 58 | >([]); 59 | 60 | expectType< 61 | TypeCheck<` 62 | 63 | let a = [true, '2']; 64 | 65 | let b = [1]; 66 | 67 | const c: boolean = a[0] === b[0]; 68 | 69 | `> 70 | >([ 71 | "7: This condition will always return 'false' since the types 'boolean | string' and 'number' have no overlap.", 72 | ]); 73 | -------------------------------------------------------------------------------- /test/Conditionals.test.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | import { expectType } from './TestUtils'; 3 | 4 | expectType< 5 | TypeCheck<` 6 | 7 | if (a) { 8 | b(); 9 | } 10 | 11 | `> 12 | >(["3: Cannot find name 'a'.", "4: Cannot find name 'b'."]); 13 | 14 | expectType< 15 | TypeCheck<` 16 | 17 | if (true) { 18 | if (false) { 19 | // empty 20 | } 21 | } 22 | 23 | `> 24 | >([]); 25 | -------------------------------------------------------------------------------- /test/Functions.test.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | import { expectType } from './TestUtils'; 3 | 4 | expectType< 5 | TypeCheck<` 6 | 7 | function foo() {} 8 | 9 | `> 10 | >([]); 11 | 12 | expectType< 13 | TypeCheck<` 14 | 15 | function foo(a, b) {} 16 | 17 | `> 18 | >([ 19 | "3: Parameter 'a' implicitly has an 'any' type.", 20 | "3: Parameter 'b' implicitly has an 'any' type.", 21 | ]); 22 | 23 | expectType< 24 | TypeCheck<` 25 | 26 | function foo(a: number, b: string) {} 27 | 28 | `> 29 | >([]); 30 | 31 | expectType< 32 | TypeCheck<` 33 | 34 | function foo(a: number, b: string) { 35 | return 5; 36 | } 37 | 38 | foo(1); 39 | 40 | `> 41 | >(['7: Expected 2 arguments, but got 1.']); 42 | 43 | expectType< 44 | TypeCheck<` 45 | 46 | function foo(a: number, b: string) { 47 | return a; 48 | } 49 | 50 | `> 51 | >([]); 52 | 53 | expectType< 54 | TypeCheck<` 55 | 56 | function foo(a: number, b: string) { 57 | return oops; 58 | } 59 | 60 | `> 61 | >(["4: Cannot find name 'oops'."]); 62 | 63 | expectType< 64 | TypeCheck<` 65 | 66 | function foo(a: number, b: string) { 67 | return 5; 68 | } 69 | 70 | foo(1, 'a'); 71 | 72 | `> 73 | >([]); 74 | 75 | expectType< 76 | TypeCheck<` 77 | 78 | function foo(a: number, b: string) { 79 | return 5; 80 | } 81 | 82 | foo(true, 'a'); 83 | 84 | `> 85 | >([ 86 | "7: Argument of type 'boolean' is not assignable to parameter of type 'number'.", 87 | ]); 88 | 89 | expectType< 90 | TypeCheck<` 91 | 92 | function foo(a: number, b: string) { 93 | if (a) { 94 | return b 95 | } 96 | 97 | return 5; 98 | } 99 | 100 | const result: number = foo(1, 'a'); 101 | 102 | `> 103 | >(["11: Type 'string | number' is not assignable to type 'number'."]); 104 | 105 | expectType< 106 | TypeCheck<` 107 | 108 | "foo"(); 109 | 110 | `> 111 | >([ 112 | "3: This expression is not callable. Type 'string' has no call signatures.", 113 | ]); 114 | 115 | expectType< 116 | TypeCheck<` 117 | 118 | foo(); 119 | 120 | `> 121 | >(["3: Cannot find name 'foo'."]); 122 | 123 | expectType< 124 | TypeCheck<` 125 | 126 | function foo() { 127 | return 1; 128 | } 129 | 130 | const a: number = foo; 131 | 132 | `> 133 | >(["7: Type '() => number' is not assignable to type 'number'."]); 134 | -------------------------------------------------------------------------------- /test/Objects.test.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | import { expectType } from './TestUtils'; 3 | 4 | expectType< 5 | TypeCheck<` 6 | 7 | const a = {}; 8 | 9 | `> 10 | >([]); 11 | 12 | expectType< 13 | TypeCheck<` 14 | 15 | const a = { 16 | hello: "world", 17 | }; 18 | 19 | const b: number = a.hello; 20 | 21 | const c = a.world; 22 | 23 | `> 24 | >([ 25 | "7: Type 'string' is not assignable to type 'number'.", 26 | "9: Property 'world' does not exist on type '{ hello: string; }'.", 27 | ]); 28 | 29 | expectType< 30 | TypeCheck<` 31 | 32 | const a = { 33 | hello: "world", 34 | }; 35 | 36 | const b = { 37 | hello: 5, 38 | } 39 | 40 | const c = [a, b]; 41 | 42 | const d: number = c[0].hello; 43 | 44 | const e: number = c[0].world; 45 | `> 46 | >([ 47 | "13: Type 'string | number' is not assignable to type 'number'.", 48 | "15: Type 'undefined' is not assignable to type 'number'.", 49 | ]); 50 | 51 | expectType< 52 | TypeCheck<` 53 | 54 | const a = { 55 | hey: "ho", 56 | }; 57 | 58 | const b = "hey"; 59 | 60 | const c: string = a[b]; 61 | 62 | `> 63 | >([]); 64 | 65 | expectType< 66 | TypeCheck<` 67 | 68 | const a = { 69 | hey: "ho", 70 | }; 71 | 72 | const b = {}; 73 | 74 | const c: string = a[b]; 75 | 76 | `> 77 | >(["9: Type '{}' cannot be used as an index type."]); 78 | -------------------------------------------------------------------------------- /test/TestUtils.ts: -------------------------------------------------------------------------------- 1 | export const expectType = (_: T): any => {}; 2 | -------------------------------------------------------------------------------- /test/Variables.test.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCheck } from '../src'; 2 | import { expectType } from './TestUtils'; 3 | 4 | expectType< 5 | TypeCheck<` 6 | 7 | let a: number = 1 8 | 9 | `> 10 | >([]); 11 | 12 | expectType< 13 | TypeCheck<` 14 | 15 | let a = 1; 16 | 17 | `> 18 | >([]); 19 | 20 | expectType< 21 | TypeCheck<` 22 | 23 | const a: number = 1 24 | 25 | `> 26 | >([]); 27 | 28 | expectType< 29 | TypeCheck<` 30 | 31 | const a = 1; 32 | 33 | `> 34 | >([]); 35 | 36 | expectType< 37 | TypeCheck<` 38 | 39 | foo; 40 | 41 | `> 42 | >(["3: Cannot find name 'foo'."]); 43 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "es2022", 5 | "lib": ["esnext"], 6 | "declaration": true, 7 | "composite": false, 8 | "isolatedModules": true, 9 | "verbatimModuleSyntax": true, 10 | "diagnostics": false, 11 | 12 | "strict": true, 13 | "emitDeclarationOnly": true, 14 | "skipLibCheck": true, 15 | "skipDefaultLibCheck": true, 16 | 17 | "moduleResolution": "node", 18 | "esModuleInterop": true, 19 | "allowSyntheticDefaultImports": true, 20 | "forceConsistentCasingInFileNames": true, 21 | "resolveJsonModule": false, 22 | 23 | "rootDirs": ["src", "test", "examples"], 24 | "outDir": "build" 25 | } 26 | } --------------------------------------------------------------------------------