{"id":28089084,"url":"https://github.com/typefox/typir","last_synced_at":"2025-05-13T12:53:38.775Z","repository":{"id":138721374,"uuid":"611305248","full_name":"TypeFox/typir","owner":"TypeFox","description":"Typir is an open source library for type checking in the web","archived":false,"fork":false,"pushed_at":"2025-04-16T11:05:37.000Z","size":4201,"stargazers_count":30,"open_issues_count":20,"forks_count":7,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-16T11:56:42.438Z","etag":null,"topics":["typesystem"],"latest_commit_sha":null,"homepage":"https://typir.org/","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/TypeFox.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"TypeFox"}},"created_at":"2023-03-08T14:54:05.000Z","updated_at":"2025-04-15T12:27:10.000Z","dependencies_parsed_at":"2024-06-17T10:08:56.729Z","dependency_job_id":"68eb2055-f82a-448c-8196-a1f22ef30e5a","html_url":"https://github.com/TypeFox/typir","commit_stats":null,"previous_names":["typefox/typir"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TypeFox%2Ftypir","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TypeFox%2Ftypir/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TypeFox%2Ftypir/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TypeFox%2Ftypir/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TypeFox","download_url":"https://codeload.github.com/TypeFox/typir/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253948110,"owners_count":21988951,"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":["typesystem"],"created_at":"2025-05-13T12:53:38.064Z","updated_at":"2025-05-13T12:53:38.749Z","avatar_url":"https://github.com/TypeFox.png","language":"TypeScript","readme":"# Typir\n\n\u003cdiv id=\"badges\" align=\"center\"\u003e\n\n  [![npm](https://img.shields.io/npm/v/typir)](https://www.npmjs.com/package/typir)\n  [![Build](https://github.com/TypeFox/typir/actions/workflows/actions.yml/badge.svg)](https://github.com/TypeFox/typir/actions/workflows/actions.yml)\n  [![Github Discussions](https://img.shields.io/badge/github-discussions-blue?logo=github)](https://github.com/TypeFox/typir/discussions)\n  [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-FFAE33?logo=gitpod)](https://gitpod.io/#https://github.com/TypeFox/typir)\n\n\u003c/div\u003e\n\n---\n\nTypir is a library for type systems and type checking for software languages in the web.\n\nTypir is OpenSource, written in TypeScript, and follows pragmatic approaches for simplifying type checking in practical language engineering projects by providing default implementations for recurring problems.\nAs a stand-alone library, Typir provides a TypeScript-API for language engineers without an additional, external DSL for formalizing types.\n\n\n## Core Features\n\nTypir provides these core features:\n\n- Predefined types:\n  - Primitives\n  - Functions (with overloading)\n  - Classes\n  - Top, bottom\n  - (more are planned)\n- Operators (with overloading)\n- Implementations for core type-checking services:\n  - Assignability\n  - Equality\n  - Conversion (implicit/coercion and explicit/casting)\n  - Type inference, i.e. determining the Typir type for a language node (e.g. an element of the current AST)\n  - Sub-typing\n  - Validation\n- Circular type definitions (e.g. `Node { children: Node[] }`)\n- Caching\n- Meaningful and customizable error messages\n- The provided default implementations are customizable by dependency injection\n\nTypir does intentionally _not_ include ...\n\n- Rule engines and constraint solving,\n  since type inference is calculated in a recursive manner and does not use unification/substitution\n- Formal proofs\n- External DSLs for formalizing types\n- Support for dynamic type systems, which do typing during the execution of the DSL.\n  Typir aims at static type systems, which do typing during the writing of the DSL.\n\n\n## NPM workspace\n\nThis repository is a NPM workspace. It contains the following packages:\n\n- [Typir](./packages/typir/README.md) is the core package of Typir with default implementations for type checking services and some predefined types. Typir is published as `typir` at [NPM](https://www.npmjs.com/package/typir?activeTab=versions).\n- [Typir-Langium](./packages/typir-langium/README.md) is the binding of Typir for [Langium](https://github.com/eclipse-langium/langium), a language workbench for developing textual DSLs in the web,\nin order to ease type checking for Langium-based languages. Typir-Langium is published as `typir-langium` at [NPM](https://www.npmjs.com/package/typir-langium?activeTab=versions).\n\nThis repository contains the following stand-alone applications, which demonstrate how to use Typir for type checking:\n\n- [LOX](./examples/lox/README.md) - static type checking for LOX, implemented with Typir-Langium\n- [OX](./examples/ox/README.md) - a reduced version of LOX, implemented with Typir-Langium\n\n\n## Tiny Typir Example\n\nBoth the LOX and OX examples have been created with Langium. Here is a very small example for using Typir with a tiny expression language, which is independent from any language workbench like Langium. We show how to use the Typir API for type checking of Tiny Typir. You can also find the example in the repository, implemented in form of an executable [test case](./packages/typir/test/api-example.test.ts).\nOur Tiny Typir language has only a few concepts (all are realized as `AstElement`s), namely numbers (`NumberLiteral`), strings (`StringLiteral`), binary expressions (`BinaryExpression`), variables (`Variable`), and assignments (`AssignmentStatement`). They are implemented in a very simple way, see for example our `BinaryExpression`:\n\n```typescript\nclass BinaryExpression extends AstElement {\n    constructor(\n        public left: AstElement,\n        public operator: string,\n        public right: AstElement,\n    ) { super(); }\n}\n```\n\nFeel free to check out the others in the [test code](./packages/typir/test/api-example.test.ts), but a little spoiler: no surprises there.\n\nLet's head into setting up the Typir type system and creating the primitive types for our NumberLiteral and StringLiteral, which is a one line of code job each, as we use the Typir's predefined Primitives factory service:\n\n```typescript\nconst typir = createTypirServices\u003cAstElement\u003e(); // \u003cAstElement\u003e specifies the root type of all language nodes\n\nconst numberType = typir.factory.Primitives.create({ primitiveName: 'number' }).inferenceRule({ filter: node =\u003e node instanceof NumberLiteral }).finish();\n\nconst stringType = typir.factory.Primitives.create({ primitiveName: 'string' }).inferenceRule({ filter: node =\u003e node instanceof StringLiteral }).finish();\n```\n\nNote that the inference rules are included in this. For the operators this is a bit longer, as we have to take care of the left and right operand and the operator of the binary expression, so we extract it and will resuse it later for both the `+` and `-` operators:\n\n```typescript\nconst inferenceRule: InferOperatorWithMultipleOperands\u003cAstElement, BinaryExpression\u003e = {\n    filter: node =\u003e node instanceof BinaryExpression,\n    matching: (node, operatorName) =\u003e node.operator === operatorName,\n    operands: node =\u003e [node.left, node.right],\n    validateArgumentsOfCalls: true, // explicitly request to check, that the types of the arguments in operator calls fit to the parameters\n};\n```\n\nWe wish to have two operators, the `+` operator, which should be overloaded to accept either two numbers to add or two strings to concatenate. This can be expressed with an array of signatures with different types for the operands and the return type of the operator. Furthermore, there is going to be a `-` operator with only one signature, since there is only subtraction of numbers. Both operators refer to the inferenceRule we defined above. `numberType` and `stringType` are the primitive types we defined above.\n\n```typescript\ntypir.factory.Operators.createBinary({ name: '+', signatures: [\n    { left: numberType, right: numberType, return: numberType },\n    { left: stringType, right: stringType, return: stringType },\n] }).inferenceRule(inferenceRule).finish();\ntypir.factory.Operators.createBinary({ name: '-', signatures: [{ left: numberType, right: numberType, return: numberType }] }).inferenceRule(inferenceRule).finish();\n```\n\nAs we'd like to be able to convert numbers to strings implicitly, we add the following line. Note that this will for example make it possible to concatenate numbers and strings with the `+` operator, though it has no signature for a number and a string parameter in the operator definition above.\n\n```typescript\ntypir.Conversion.markAsConvertible(numberType, stringType, 'IMPLICIT_EXPLICIT');\n```\n\nFurthermore we can specify how Typir should infer the variable type. We decided that the type of the variable should be the type of its initial value. Typir internally considers the inference rules for primitives and operators as well, when recursively inferring the given AstElement.\n\n```typescript\ntypir.Inference.addInferenceRule(node =\u003e {\n    if (node instanceof Variable) {\n        return node.initialValue; // the type of the variable is the type of its initial value\n    }\n    return InferenceRuleNotApplicable;\n});\n```\n\nFinally, we add a type related validation rule for our small example: In case we have an AssignmentStatement, we check whether the type to be assigned is an assignable match for the variable type. We can do that with a custom message. An error with this message will show up for example when we try to assign the string literal \"hello\" to a number variable. It will not show up in case we assign the number literal 123 to a string variable, as we have defined the implicit conversion above.\n\n```typescript\ntypir.validation.Collector.addValidationRule((node, accept) =\u003e {\n    if (node instanceof AssignmentStatement) {\n        typir.validation.Constraints.ensureNodeIsAssignable(node.right, node.left, accept, (actual, expected) =\u003e ({ message:\n            `The type '${actual.name}' is not assignable to the type '${expected.name}'.` }));\n    }\n});\n```\n\nWrapping this up, these are the test examples for the language usage with the expected type checking outcome:\n\n```typescript\n// 2 + 3 =\u003e OK\nconst example1 = new BinaryExpression(new NumberLiteral(2), '+', new NumberLiteral(3));\nexpect(typir.validation.Collector.validate(example1)).toHaveLength(0);\n\n// 2 + \"3\" =\u003e OK\nconst example2 = new BinaryExpression(new NumberLiteral(2), '+', new StringLiteral('3'));\nexpect(typir.validation.Collector.validate(example2)).toHaveLength(0);\n\n// 2 - \"3\" =\u003e wrong\nconst example3 = new BinaryExpression(new NumberLiteral(2), '-', new StringLiteral('3'));\nconst errors1 = typir.validation.Collector.validate(example3);\nconst errorStack = typir.Printer.printTypirProblem(errors1[0]); // the problem comes with detailed \"sub-problems\"\nexpect(errorStack).includes(\"The parameter 'right' at index 1 got a value with a wrong type.\");\nexpect(errorStack).includes(\"For property 'right', the types 'string' and 'number' do not match.\");\n\n// 123 is assignable to a string variable\nconst varString = new Variable('v1', new StringLiteral('Hello'));\nconst assignNumberToString = new AssignmentStatement(varString, new NumberLiteral(123));\nexpect(typir.validation.Collector.validate(assignNumberToString)).toHaveLength(0);\n\n// \"123\" is not assignable to a number variable\nconst varNumber = new Variable('v2', new NumberLiteral(456));\nconst assignStringToNumber = new AssignmentStatement(varNumber, new StringLiteral('123'));\nconst errors2 = typir.validation.Collector.validate(assignStringToNumber);\nexpect(errors2[0].message).toBe(\"The type 'string' is not assignable to the type 'number'.\");\n```\n\n## Resources\n\nTypir is presented in these talks:\n\n- [LangDev'24](https://langdevcon.org/2024/program#26): [Video](https://www.youtube.com/watch?v=CL8EbJYeyTE), [slides](./resources/talks/2024-10-17-LangDev.pdf) (2024-10-17)\n- [OCX/EclipseCon'24](https://www.ocxconf.org/event/778b82cc-6834-48a4-a58e-f883c5a7b8c9/agenda?session=23b97df9-0435-4fab-8a01-e0a9cf3e3831\u0026shareLink=true): [Video](https://www.youtube.com/watch?v=WLzXAhcl-aY\u0026list=PLy7t4z5SYNaRRGVdF83feN-_uHLwvGvgw\u0026index=23), [slides](./resources/talks/2024-10-24-EclipseCon.pdf) (2024-10-24)\n\n\n## Roadmap\n\nThe roadmap of Typir is organized with [milestones in GitHub](https://github.com/TypeFox/typir/milestones).\n\nThe roadmap includes, among other, these features:\n\n- More predefined types: structurally typed classes, lambdas, generics, constrained primitive types (e.g. numbers with upper and lower bound), ...\n- Calculate types, e.g. operators whose return types depend on their current input types\n- Simplified API for custom types\n\nFor the released versions of Typir, see the [CHANGELOG.md](./CHANGELOG.md).\n\n\n## Contributing\n\nPlease read the [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.\n\nWe also have a release process described in [RELEASE.md](./RELEASE.md).\n\n\n## License\n\nTypir is fully [MIT licensed](./LICENSE).\n","funding_links":["https://github.com/sponsors/TypeFox"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftypefox%2Ftypir","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftypefox%2Ftypir","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftypefox%2Ftypir/lists"}