{"id":13441704,"url":"https://github.com/fabiandev/ts-runtime","last_synced_at":"2025-04-04T13:13:57.644Z","repository":{"id":41142729,"uuid":"78781448","full_name":"fabiandev/ts-runtime","owner":"fabiandev","description":"Runtime Type Checks for TypeScript","archived":false,"fork":false,"pushed_at":"2020-11-10T21:48:55.000Z","size":34713,"stargazers_count":493,"open_issues_count":13,"forks_count":5,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-03-28T12:04:36.029Z","etag":null,"topics":["javascript","runtime","typechecker","typescript"],"latest_commit_sha":null,"homepage":"https://fabiandev.github.io/ts-runtime/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fabiandev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-12T19:55:31.000Z","updated_at":"2025-02-21T20:19:07.000Z","dependencies_parsed_at":"2022-09-09T11:10:29.389Z","dependency_job_id":null,"html_url":"https://github.com/fabiandev/ts-runtime","commit_stats":null,"previous_names":[],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabiandev%2Fts-runtime","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabiandev%2Fts-runtime/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabiandev%2Fts-runtime/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabiandev%2Fts-runtime/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fabiandev","download_url":"https://codeload.github.com/fabiandev/ts-runtime/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247179287,"owners_count":20897010,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","runtime","typechecker","typescript"],"created_at":"2024-07-31T03:01:37.181Z","updated_at":"2025-04-04T13:13:57.621Z","avatar_url":"https://github.com/fabiandev.png","language":"TypeScript","readme":"[![Build Status](https://travis-ci.org/fabiandev/ts-runtime.svg?branch=master)](https://travis-ci.org/fabiandev/ts-runtime)\n[![Coverage Status](https://coveralls.io/repos/github/fabiandev/ts-runtime/badge.svg?branch=master)](https://coveralls.io/github/fabiandev/ts-runtime?branch=master)\n\n# ts-runtime\n\nA package for generating runtime type checks from TypeScript type annotations for JavaScript, using the TypeScript compiler API.  \n  \nPlease note, that this package is still experimental and resulting code is not intended to be used in production.\nIt is a proof of concept for adding runtime type checks by making use of the recently published TypeScript transformation API.\nFeel free to report bugs and to make suggestions. Pull requests are also very welcome.\n\n## Playground\n\nYou can try `ts-runtime` on the playground: https://fabiandev.github.io/ts-runtime/\n\n## Quick Start\n\n```sh\n$ yarn global add ts-runtime\n$ tsr --help\n```\n\n\u003e You can also use `npm -g install ts-runtime`.\n\n## Credits\n\nType reflections and assertions for the runtime environment are being made possible by [flow-runtime](https://github.com/codemix/flow-runtime), a runtime type system for JavaScript.\n\n## Transformations\n\nMost of explicit type annotations will be reflected (and checked) at runtime. Checks for implicitly inferred types may be added in a future release. In the following section the most important cases are documented.\n\n### Source File\n\nOn top of every source file, the runtime type checking library is imported.\n\n```ts\nimport t from 'ts-runtime/lib';\n```\n\n### Variables\n\nIt will be distinguished between reassignable declarations (`var` and `let`) and constant declarations (`const`).\n\n#### Reassignable Declarations\n\nA variable declaration that may be reassigned at runtime as the following:\n\n```ts\nlet num: number;\nnum = \"Hello World!\";\n```\n\nwill be reflected and checked as shown below:\n\n```js\nlet _numType = t.number(), num;\nnum = _numType.assert(\"Hello World!\")\n```\n\n#### Constant Declarations\n\nA constant declaration does only need to be checked when declared, and no additional variable holding the variable's type, has to be introduced.\n\n```ts\nconst num: number = \"Hello World!\";\n```\n\nThe const declaration from above, results in:\n\n```js\nconst num = t.number().assert(\"Hello World!\");\n```\n\n### Assertions\n\nIn TypeScript the above assignments would already be flagged by the compiler. By using the type assertion `as any`, any assignment will be allowed but still caught at runtime with this package.\n\n```ts\ntrue as any as number;\n```\n\nThe above assertion may not be a real world example, but there are situations where similar things happen. Imagine calling a function, that returns `any`. While *you know* that the returned value will be a number, you also want the TypeScript compiler to be aware of it and you assert it `as number`. Probably this function call is from an external library and a bug is introduced that a boolean value is returned instead, which will remain unnoticed unless checked at runtime:\n\n```js\nt.number().assert(true);\n```\n\n### Functions\n\nFunction parameters and its return value will be checked as well, and functions will be annotated to extract its type reflection at runtime.\n\n#### Function Declarations\n\nThis function simply creates a number from a string.\n\n```ts\nfunction getNumberFromString(str: string): number {\n  return Number(str);\n}\n```\n\nThe code below is the result, with runtime type checks inserted.\n\n```js\nfunction getNumberFromString(str) {\n  let _strType = t.string();\n  const _returnType = t.return(t.number());\n  t.param(\"str\", _strType).assert(str);\n  return _returnType.assert(Number(str));\n}\n```\n\nBy default the function is also annotated, to get the type reflection of the object at runtime with type queries:\n\n```js\nt.annotate(getNumberFromString, t.function(t.param(\"str\", t.string()), t.return(t.number())));\n```\n\n\u003e Annotations can be turned off, by setting the `annotate` option to `false` or using the `--noAnnotate` flag with the CLI.\n\n#### Function Expressions\n\nAlso function expressions will be transformed:\n\n```ts\nconst getNumberFromString = function(str: string): number {\n  return Number(str);\n}\n```\n\nThis declaration will be converted to:\n\n```js\nconst getNumberFromString = function (str) {\n  let _strType = t.string();\n  const _returnType = t.return(t.number());\n  t.param(\"str\", _strType).assert(str);\n  return _returnType.assert(Number(str));\n};\n```\n\nAgain, by default the function is annotated like so:\n\n```js\nconst getNumberFromString = t.annotate(function (str) {\n  // function body\n}, t.function(t.param(\"str\", t.string()), t.return(t.number())));\n```\n\n#### Arrow Functions\n\nArrow function are also supported, with a similar result to function expressions.\nIf runtime checks have to be inserted into an arrow function without a body, ts-runtime generates it for you:\n\n```ts\nconst getNumberFromString = (str: string): number =\u003e Number(str);\n```\n\nThis one-liner is great, but in order to assert the values it is transformed to the following:\n\n```js\nconst getNumberFromString = (str) =\u003e {\n  let _strType = t.string();\n  const _returnType = t.return(t.number());\n  t.param(\"str\", _strType).assert(str);\n  return _returnType.assert(Number(str));\n};\n```\n\nOf course, also arrow functions are annotated by default:\n\n```js\nconst getNumberFromString = t.annotate((str) =\u003e {\n  // arrow function body\n}, t.function(t.param(\"str\", t.string()), t.return(t.number())));\n```\n\n### Type Queries\n\nIn the following example, TypeScript gets the type of a variable and uses it as type for another variable declaration.\n\n```ts\nlet num = 10;\nlet numType: typeof num = \"Hello World!\";\n```\n\nIn ts-runtime this will be transformed to:\n\n```js\nlet num = 10;\nlet _myNumType = t.typeOf(num), myNum = _myNumType.assert(\"Hello World!\");\n```\n\n\u003e Please note, that `num` is not reflected and asserted, because it lacks an explicit type annotation.\n\n### Enums\n\nThe TypeScript compiler option `preserveConstEnums` will be always set to `true` by ts-runtime. A warning in the console will let you know.\n\n#### Enum Declarations\n\n```ts\nenum Action {\n  None,\n  Save,\n  Update\n}\n```\n\nThe enum from above will be transformed to the following by TypeScript:\n\n```js\nvar Action;\n(function (Action) {\n  Action[Action[\"None\"] = 0] = \"None\";\n  Action[Action[\"Save\"] = 1] = \"Save\";\n  Action[Action[\"Update\"] = 2] = \"Update\";\n})(Action || (Action = {}));\n```\n\nand annotated by ts-runtime with the reflection below:\n\n```ts\nt.annotate(Action, t.enum(t.enumMember(0), t.enumMember(1), t.enumMember(2)));\n```\n\n#### Enum References\n\nWhen using the enum as a type reference, only the numbers `0`, `1`, and `2` can be assigned to `action`:\n\n```ts\nlet action: Action;\n```\n\n```js\nlet _actionType = t.enumRef(Action), action;\n```\n\nThe same is true when using a specific enum members as reference. In this example only the number `1` can be assigned to `saveAction`:\n\n```ts\nlet saveAction: Action.Save;\n```\n\n```js\nlet _saveActionType = t.enumMember(Action.Save), saveAction;\n```\n\n### Type Aliases\n\nType aliases are removed entirely by the TypeScript compiler.\n\n```ts\ntype MyType = {\n  property: string;\n  optional?: number;\n  method: (param: boolean) =\u003e void;\n}\n```\n\nThe type alias declaration from above will be replaced with the following reflection:\n\n```js\nconst MyType = t.type(\"MyType\", t.object(\n  t.property(\"property\", t.string()),\n  t.property(\"optional\", t.number(), true),\n  t.property(\"method\", t.function(t.param(\"param\", t.boolean()), t.return(t.void())))\n));\n```\n\n\u003e Self references are supported.\n\n### Interfaces\n\nAlso interfaces would be compiled away.\n\n```ts\ninterface BaseInterface {\n  [index: string]: any;\n}\n\ninterface MyInterface extends BaseInterface {\n  prop: string;\n}\n```\n\nWith ts-runtime they will be replaced with a reflection:\n\n```js\nconst BaseInterface = t.type(\"BaseInterface\", t.object(\n  t.indexer(\"index\", t.string(), t.any()))\n);\n\nconst MyInterface = t.type(\"MyInterface\", t.intersect(t.ref(BaseInterface), t.object(\n  t.property(\"prop\", t.string())\n)));\n```\n\n### Classes\n\nClasses are transformed with support for properties, static properties, static and non-static methods, deriving from other classes (`extends`), implementing interfaces (`implements`), as well as method overloading.\n\n```ts\nclass MyClass {\n  method(param?: number): void {\n\n  }\n}\n```\n\nAt this point, only a very minimal class transformation, with a single method, is shown:\n\n```js\nclass MyClass {\n  method(param) {\n    let _paramType = t.number();\n    const _returnType = t.return(t.void());\n    t.param(\"param\", _paramType, true).assert(param);\n  }\n}\n```\n\nBy default also classes are annotated:\n\n```js\n@t.annotate(t.class(\"MyClass\", \n  t.property(\"method\", t.function(t.param(\"param\", t.number(), true), t.return(t.void())))\n))\nclass MyClass {\n  // class body\n}\n```\n\n### Overloading\n\nMethod overloading is supported for type aliases, interfaces and classes, and generates union types based on the overloads.\n\n```ts\nclass MyInterface {\n  method(param: number): string;\n  method(param: boolean): string;\n  method(param: any): any {\n    // implementation\n  }\n}\n```\n\nWhile all overloads are considered (excluding merged declarations) when generating a reflection, the implementation itself is ignored:\n\n```js\n@t.annotate(t.class(\"MyInterface\",\n  t.property(\"method\", t.function(\n    t.param(\"param\", t.union(t.number(), t.boolean())), t.return(t.string()))\n  )\n))\nclass MyInterface {\n  method(param) {\n    // implementation\n  }\n}\n\n```\n\n### Generics\n\nGenerics are supported for functions, classes, interfaces and type aliases.\n\n```ts\nfunction asArray\u003cT\u003e(val: T): T[] {\n  return [val];\n}\n```\n\nThe above snippet shows a simple function that makes use of generics to specify\nthe return type, which results in the following transformation:\n\n```js\nfunction asArray(val) {\n  const T = t.typeParameter(\"T\");\n  let _valType = t.flowInto(T);\n  const _returnType = t.return(t.array(T));\n  t.param(\"val\", _valType).assert(val);\n  return _returnType.assert([val]);\n}\n```\n\n### Externals and Ambient Declarations\n\nWe were seeing a couple of different transformations based on local variables.\nWhat about external packages, declaration files and ambient declarations?\nThey are collected and emitted to a single file.\n\n#### Externals\n\nImagine the following type reference:\n\n```ts\nimport * as ts from 'typescript';\nlet flowNode: ts.FlowNode;\n```\n\nIt points to the interface `FlowNode` inside `typescript.d.ts`:\n\n```ts\ninterface FlowNode {\n  flags: FlowFlags;\n  id?: number;\n}\n```\n\nThe reference is removed and replaced by a string. This string holds the fully\nqualified name of the reference, and the hashed file name as a suffix, to\nprevent naming clashes:\n\n```js\nlet _flowNodeType = t.ref(\"ts.FlowNode.82613696\"), flowNode;\n```\n\nThe actual reflections go into a single file (`tsr-declaration.js` by default):\n\n```js\nt.declare(t.type(\"ts.FlowFlags.82613696\", t.enum(/* enum members */)));\nt.declare(t.type(\"ts.FlowNode.82613696\", t.object(/* properties */)));\n```\n\n\u003e By default declarations from built in libs (such as DOM, or ES6) are not reflected, but inferred at runtime.\n\n#### Declarations\n\nAlso local declarations will be included in `tsr-declarations.js`:\n\n```ts\ndeclare module MyModule {\n  class MyClass {\n\n  }\n}\n```\n\nThe code from above will be reflected as follows:\n\n```js\nt.declare(t.class(\"MyModule.MyClass.3446180452\", t.object()));\n```\n\n\u003e The generated file will be located in the common directory of all entry files\nor in the root of `outDir` or `outFile`. For some controls regarding this file,\nhave a look at the options.\n\n## Limitations\n\n- Only `as number` syntax for type assertions (no angle-bracket syntax: `\u003cnumber\u003e`).\n- No reflection of mapped types, indexed access types and type operators yet.\n- `readonly` is currently only checked for classes.\n- Class visibility modifiers are not asserted.\n- Class type parameters are only checked when extending, at this time.\n- Types with self references and generics are not asserted correctly.\n- No class expressions (`const A = class { }`), only class declarations (`class A { }`) can be used.\n- `ExpressionWithTypeArguments` can only contain `PropertyAccessExpression`s as expression with an `Identifier` as name, recursively.\n- No JSX support.\n\n## Options\n\n#### noAnnotate\nType: `boolean`  \nDefault: false  \n\nSpecifies if classes and function should be annotated.\n\n#### compilerOptions\nType: `ts.CompilerOptions`  \nDefault:  \n```js\n{\n  moduleResolution: ts.ModuleResolutionKind.NodeJs,\n  module: ts.ModuleKind.ES2015,\n  target: ts.ScriptTarget.ES2015,\n  lib: [\"lib.es2015.d.ts\"],\n  strictNullChecks: true,\n  experimentalDecorators: true,\n  sourceMap: false,\n  removeComments: true,\n  preserveConstEnums: true,\n}\n```\n\n\u003e The option preserveConstEnum will always be set to true by ts-runtime.\n\n#### declarationFileName\nType: `string`  \nDefault: \"tsr-declarations\"  \n\nThe file name where all external and ambient declarations will be written to.\nExcludes a path or an extension.\n\n#### excludeDeclarationFile\nType: `boolean`  \nDefault: false  \n\nSpecifies if the generated file should be imported on top of every entry file.\n\n#### excludeLib\nType: `boolean`  \nDefault: false  \n\nSpecifies if the library import on top of every file should be omitted.\n\n#### force\nType: `boolean`  \nDefault: false  \n\nTry to continue if TypeScript compiler diagnostics occurred.\n\n#### keepTemp\nType: `boolean`  \nDefault: false  \n\nDo not delete temporary files on finish.\n\n#### tempFolderName\nType: `string`  \nDefault: \".tsr\"  \n\nName of the directory, where temporary files should be written to.\n\n#### libNamespace\nType: `string`  \nDefault: \"\"  \n\nPrefix for the default library import.\n\n#### libIdentifier\nType: `string`  \nDefault: \"t\"  \n\nIdentifier of the default library import, prefixed by its namespace.\nLooks like the following by default\n\n```ts\nimport t from \"ts-runtime/lib\";\n```\n\n#### libIdentifier\nType: `boolean`  \nDefault: false  \n\nBy default, built in libraries, such as DOM or ES6, are not reflected, but inferred at runtime.\n\n#### declarationPrefix\nType: `string`  \nDefault: \"_\"  \n\nIf new variables are introduced while transforming, they will be prefixed with\nthis specified string.\n\n#### moduleAlias\nType: `boolean`  \nDefault: false  \n\nAdds `import \"module-alias/register\";` on top of every file.\n\n#### stackTraceOutput\nType: `number`  \nDefault: 3  \n\nLimit the output of the stack trace. This only takes effect when using the CLI.\n\n#### log\nType: `boolean`  \nDefault: true  \n\nLog messages to the console. This option is not available via the CLI.\n\n## API\n\nIt is easy to make use of ts-runtime via Node.js.\n`entryFiles` is a `string[]` and an `Options` object may optionally be passed\nas a second parameter.\n\n```ts\nimport { transform } from 'ts-runtime';\ntransform(entryFiles);\n```\n\nIt is also possible to listen for various events:\n\n```ts\nimport { transform, bus } from 'ts-runtime';\n\nbus.on(bus.events.START, () =\u003e {\n  // callback if processing is about to start\n});\n\ntransform(entryFiles, { log: false });\n```\n\n## CLI\n\n```\n  Usage: tsr \u003cfile...\u003e [options]\n\n  ---------  ts-runtime  ---------\n  Turns TypeScript type assertions\n  into runtime type checks for you\n  --------------------------------\n\n  Options:\n\n    -h, --help                               output usage information\n    -v, --version                            output the version number\n    -a, --noAnnotate                         do not annotate classes and functions\n    -c, --tsConfig \u003cpath\u003e                    use the compiler options of the given tsconfig.json\n    -C, --compilerOptions \u003ccompilerOptions\u003e  set TypeScript compiler options. defaults to \"{}\"\n    -d, --declarationFileName \u003cfileName\u003e     set file name for global declarations. defaults to \"tsr-declarations\"\n    -e, --excludeDeclarationFile             do not automatically import ambient declarations in the entry file. default to false\n    -E, --excludeLib                         do not automatically import the runtime library. defaults to false\n    -f, --force                              try to finish on TypeScript compiler error. defaults to false\n    -F, --fast                               no fancy status for the command line, but faster processing. defaults to false\n    -l, --libIdentifier \u003cname\u003e               lib import name. defaults to \"t\"\n    -L, --libDeclarations                    reflect declarations from global libs (e.g. DOM). defaults to false\n    -m, --moduleAlias                        import package module-alias on top of every file.\n    -n, --libNamespace \u003cnamespace\u003e           prefix for lib and code additions. defaults to \"\"\n    -p, --declarationPrefix \u003cnamespace\u003e      prefix for added variables. defaults to \"_\"\n    -s, --stackTraceOutput \u003climit\u003e           output a specified number of lines of the stack trace. defaults to 3\n\n  Examples:\n\n    $ tsr entry.ts --force\n    $ tsr src/entry1 bin/entry2 lib/entry3\n    $ tsr entry.ts -c tsconfig.json\n```\n\n## Building\n\n```sh\n$ git checkout https://github.com/fabiandev/ts-runtime.git\n$ cd ts-runtime\n$ yarn build\n```\n","funding_links":[],"categories":["HarmonyOS","TypeScript"],"sub_categories":["Windows Manager"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabiandev%2Fts-runtime","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffabiandev%2Fts-runtime","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabiandev%2Fts-runtime/lists"}