{"id":15432084,"url":"https://github.com/jack-williams/contracts-ts","last_synced_at":"2025-04-19T17:45:36.379Z","repository":{"id":57206637,"uuid":"130737338","full_name":"jack-williams/contracts-ts","owner":"jack-williams","description":"Higher-order Contracts for Intersection and Union Types","archived":false,"fork":false,"pushed_at":"2020-03-16T17:10:12.000Z","size":88,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-06T20:41:22.393Z","etag":null,"topics":["contracts","intersection-types","typescript","union-types"],"latest_commit_sha":null,"homepage":"","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/jack-williams.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":"2018-04-23T18:09:12.000Z","updated_at":"2020-08-26T18:11:11.000Z","dependencies_parsed_at":"2022-09-08T14:22:21.398Z","dependency_job_id":null,"html_url":"https://github.com/jack-williams/contracts-ts","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jack-williams%2Fcontracts-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jack-williams%2Fcontracts-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jack-williams%2Fcontracts-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jack-williams%2Fcontracts-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jack-williams","download_url":"https://codeload.github.com/jack-williams/contracts-ts/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249752005,"owners_count":21320411,"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":["contracts","intersection-types","typescript","union-types"],"created_at":"2024-10-01T18:25:05.606Z","updated_at":"2025-04-19T17:45:36.363Z","avatar_url":"https://github.com/jack-williams.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `contracts-ts`\n\nContracts for higher-order intersection and union. Read the paper behind the implementation [here](https://dl.acm.org/citation.cfm?doid=3288538.3276504)\n\n### Getting started\n\nEnsure `node` and `npm` are installed and run the following command at the root of the directory:\n\n```\nnpm install\n```\n\nTo build the project run:\n\n```\nnpm run build\n```\n\nthis will compile the TypesScript files into `dist/`. The main file will be `./dist/index.js`.\n\nTo run the tests:\n\n```\nnpm run test\n```\n\n### Examples\n\nThe `examples` directory contains a series of examples along with commentary. The aim of these files is to give an introduction to using the library, and also discuss some of the details of blame assignment for intersection and union.\n\n### Usage\n\n#### Importing the library\n\nPoint your client at the `dist/index.js` file. In many cases it is helpful to create aliase for the type constructors and pre-defined base types.\n\n```js\nconst contract = require(\"path to dist/index.js\");\nconst Base = contract.Base;\nconst Type = contract.Type;\n```\n\n#### Constructing Types\n\n- **Base Types** are constructed from predicates (functions that take a value and return a `boolean`). For example:\n```javascript\nconst zeroType = Type.makeBaseType(\"zero\", x =\u003e x === 0);\n```\nThere is one siginifcant caveat to making base types. Blame assignment will be incorrect for base types that apply their argument, for example:\n```javascript\nconst zeroFunction = Type.makeBaseType(\"zeroFunction\", f =\u003e f(0) === 0);\n```\nThis is due to complexities in intersection and union contracts. Keil and Thiemann [1] present a solution to this problem but it is non-trivial to implement in practice.\n\n- **Function Types** are constructed from a sequence of argument types and a return type. For example:\n```javascript\nconst fnType = Type.fun([Base.number, zeroType], Base.string);\n```\ndefines the contract type for the function that accepts two arguments, a number and 0, and returns a string. There is not support for optional or variadic functions, although optional arguments can be implemented using a union type with `null` and `undefined`.\n\nFunction types do not assert that the value is a function, only that domain are codomain are respected when the value is applied. To create a traditional function contract we can use `and`.\n\n- **Branching Types** are constructed from two types. There are three branching types: and, intersection, and union. For example:\n```javascript\n// A type that checks the value is a function, and it respects the type [number,0] -\u003e string.\nconst aFunctionType = Type.and(Base.function, fnType);\n// Creates an intersection of function types, acting like an overloaded function.\nconst overloaded = Type.intersection(aFunctionType, Type.fun([Base.string, Base.string], Base.boolean));\n// Creates a 'optional' number, something that can be a number or undefined.\nconst maybeNumber = Type.union(Base.number, Base.undefined);\n```\n\n#### Applying contracts\nThe main way to apply a contract is using `assert`. For example:\n```javascript\nconst three = contract.assert(3, maybeNumber);\n```\nwhich will apply the `maybeNumber` contract to 3, returning the result. The assert function can be given a string to identify annotate the blame label:\n```javascript\nconst maybeThree = contract.assert(undefined, \"my blame label\", maybeNumber);\n```\n\nFunction can be wrapped by applying a function contract:\n```javascript\nfunction foo(x,y) {\n    if(typeof x === \"string\") {\n        return x.length \u003e y.length\n    }\n    return x + y + x;\n}\n\n// Wrap the function\nfoo = contract.assert(foo, \"contract for foo\", overloaded);\n\n// Apply the wrapped function\nfoo(\"a\", \"b\");\nfoo(4, 0);\n```\n\n#### Blame Errors\nWhen a contract is violated a blame error is throw, indicating if the blame was positive (inside the contract), or negative (due to the context of the contract). For example:\n\n```javascript\nfoo(4, 1);\n\n/*\nNegative blame @ label Symbol(contract for foo)\nReason: Value not of expected type zeroType, received number.\nType: {\n  \"branch\": \"intersection\",\n  \"left\": {\n    \"branch\": \"and\",\n    \"left\": \"function\",\n    \"right\": {\n      \"args\": [\n        \"number\",\n        \"zeroType\"\n      ],\n      \"ret\": \"string\"\n    }\n  },\n  \"right\": {\n    \"args\": [\n      \"string\",\n      \"string\"\n    ],\n    \"ret\": \"boolean\"\n  }\n}\n*/\n```\n\n\n[1] Matthias Keil and Peter Thiemann, Blame assignment for higher-order contracts with intersection and union\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjack-williams%2Fcontracts-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjack-williams%2Fcontracts-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjack-williams%2Fcontracts-ts/lists"}