-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2b4a3c6
Showing
16 changed files
with
10,672 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
node_modules | ||
dist | ||
.eslintrc.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
module.exports = { | ||
env: { | ||
browser: true, | ||
es2021: true, | ||
node: true, | ||
jest: true, | ||
}, | ||
extends: [ | ||
'eslint:recommended', | ||
'plugin:@typescript-eslint/eslint-recommended', | ||
'plugin:@typescript-eslint/recommended', | ||
'prettier', | ||
'prettier/@typescript-eslint', | ||
'plugin:prettier/recommended', | ||
], | ||
parser: '@typescript-eslint/parser', | ||
parserOptions: { | ||
ecmaVersion: 12, | ||
sourceType: 'module', | ||
}, | ||
plugins: ['@typescript-eslint'], | ||
rules: {}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: Build | ||
on: | ||
push: | ||
branches: [ master ] | ||
pull_request: | ||
branches: [ master ] | ||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
|
||
strategy: | ||
matrix: | ||
node-version: [10.x, 12.x, 14.x] | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Use Node.js ${{ matrix.node-version }} | ||
uses: actions/setup-node@v1 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
- run: npm ci | ||
- run: npm run build | ||
- run: npm test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/node_modules/ | ||
/.idea/ | ||
/index.js | ||
/index.d.ts | ||
/index.test.d.ts | ||
/index.test.js | ||
/index.test-d.d.ts | ||
/index.test-d.js | ||
/coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// The configuration object for [lint-staged](https://www.npmjs.com/package/lint-staged) | ||
module.exports = { | ||
// Lint source with [ESLint](https://www.npmjs.com/package/eslint) | ||
'index.ts': 'eslint --fix', | ||
// Format all files with [prettier](https://www.npmjs.com/package/prettier). | ||
'*.{ts,js,json,md}': 'prettier --write', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/.idea/ | ||
/.eslintrc.js | ||
/.gitignore | ||
/.lintstagedrc.js | ||
/.versionrc.json | ||
/jest.config.js | ||
/tsconfig.json | ||
/tsconfig.test.json | ||
/index.ts | ||
/index.test.ts | ||
/index.test.d.ts | ||
/index.test.js | ||
/index.test-d.ts | ||
/index.test-d.d.ts | ||
/index.test-d.js | ||
/coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"header": "", | ||
"types": [ | ||
{ | ||
"type": "feat", | ||
"section": "Features" | ||
}, | ||
{ | ||
"type": "fix", | ||
"section": "Bug Fixes" | ||
}, | ||
{ | ||
"type": "chore", | ||
"hidden": true | ||
}, | ||
{ | ||
"type": "docs", | ||
"hidden": true | ||
}, | ||
{ | ||
"type": "style", | ||
"hidden": true | ||
}, | ||
{ | ||
"type": "refactor", | ||
"section": "Code Refactoring" | ||
}, | ||
{ | ||
"type": "perf", | ||
"section": "Performance Improvements" | ||
}, | ||
{ | ||
"type": "test", | ||
"hidden": true | ||
}, | ||
{ | ||
"type": "build", | ||
"hidden": true | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
# ts-code-contracts <a href="https://www.github.com/JanMalch/ts-code-contracts"><img src="https://user-images.githubusercontent.com/25508038/63974103-75242700-caac-11e9-8ca4-71cc5b905e90.png" width="90" height="90" align="right"></a> | ||
|
||
[![npm](https://badgen.net/bpm/v/ts-code-contracts)](https://www.npmjs.com/package/ts-code-contracts) | ||
[![Build](https://github.com/JanMalch/ts-code-contracts/workflows/Build/badge.svg)](https://github.com/JanMalch/ts-code-contracts/workflows/Build) | ||
[![coverage](https://img.shields.io/badge/coverage-100%25-success)](https://github.com/JanMalch/ts-code-contracts/blob/master/jest.config.js#L14-L17) | ||
[![minified + gzip](https://badgen.net/bundlephobia/minzip/ts-code-contracts)](https://bundlephobia.com/result?p=ts-code-contracts) | ||
|
||
_Code contracts to improve your TypeScript code._ | ||
|
||
## Installation & Usage | ||
|
||
``` | ||
npm i ts-code-contracts | ||
``` | ||
|
||
> Requires TypeScript^3.7 | ||
You can now import the following functions `from 'ts-code-contracts'`: | ||
|
||
- Contracts | ||
- [`requires` for preconditions](#requires) | ||
- [`checks` for illegal states](#checks) | ||
- [`ensures` for postconditions](#ensures) | ||
- [`unreachable` for unreachable code branches](#unreachable) | ||
- [`asserts` for any other assertion](#asserts) | ||
- Utils | ||
- [`error` to make code more concise](#error) | ||
- [`isDefined` type guard](#isdefined) | ||
- [`useIf` for assignments](#useif) | ||
|
||
Make sure to read the `@example`s in the documentation below | ||
or refer to the [test cases](https://github.com/JanMalch/ts-code-contracts/blob/master/index.test.ts#L137-L165) | ||
and [typing assistance](https://github.com/JanMalch/ts-code-contracts/blob/master/index.test-d.ts#L54-L65)! | ||
|
||
## Contracts | ||
|
||
The following functions are the core of this library. | ||
They are just handy shorthands to throw an error, if the given condition is not met. | ||
And yet they greatly help the compiler and the readability of your code. Make sure to fail fast! | ||
|
||
### `requires` | ||
|
||
Use it for preconditions, like validating arguments. | ||
|
||
```ts | ||
/** | ||
* Requires the given condition to be met, if not a `PreconditionError` will be thrown. | ||
* Use it to verify argument values. | ||
* @param condition the condition that must be `true` | ||
* @param message an optional message for the error | ||
* @see PreconditionError | ||
* @example | ||
* function myFun(name: string) { | ||
* requires(name.length > 10, 'Name must be longer than 10 chars'); | ||
* } | ||
*/ | ||
export function requires( | ||
condition: boolean, | ||
message: string = 'Unmet precondition' | ||
): asserts condition; | ||
``` | ||
|
||
### `checks` | ||
|
||
Use it to check for an illegal state. | ||
|
||
```ts | ||
/** | ||
* Checks that the given condition is met, if not a `IllegalStateError` will be thrown. | ||
* Use it to verify that the object is in a correct state. | ||
* @param condition the condition that must be `true` | ||
* @param message an optional message for the error | ||
* @see IllegalStateError | ||
* @example | ||
* class Socket { | ||
* send(data: Data) { | ||
* check(this.isOpen, 'Socket must be open'); | ||
* } | ||
* } | ||
*/ | ||
export function checks( | ||
condition: boolean, | ||
message: string = 'Callee invariant violation' | ||
): asserts condition; | ||
``` | ||
|
||
### `ensures` | ||
|
||
Use it to verify that your code behaved correctly. | ||
|
||
```ts | ||
/** | ||
* Ensures that the given condition is met, if not a `PostconditionError` will be thrown. | ||
* Use it to verify that your function behaved correctly. | ||
* @param condition the condition that must be `true` | ||
* @param message an optional message for the error | ||
* @see PostconditionError | ||
* @example | ||
* async function myFun() { | ||
* await post({ id: 0, name: 'John' }); | ||
* const entity = await get(0); | ||
* ensures(isDefined(entity), 'Failed to persist entity on server'); | ||
* } | ||
*/ | ||
export function ensures( | ||
condition: boolean, | ||
message: string = 'Unmet postcondition' | ||
): asserts condition; | ||
``` | ||
|
||
### `unreachable` | ||
|
||
Use it to assert that a code branch is unreachable. | ||
|
||
```ts | ||
/** | ||
* Asserts that a code branch is unreachable. If it is, the compiler will throw a type error. | ||
* If this function is reached at runtime, an error will be thrown. | ||
* @param _value a value | ||
* @example | ||
* function myFun(foo: MyEnum): string { | ||
* switch(foo) { | ||
* case MyEnum.A: return 'a'; | ||
* case MyEnum.B: return 'b'; | ||
* default: unreachable(foo); | ||
* } | ||
* } | ||
*/ | ||
export function unreachable(_value: never): never; | ||
``` | ||
|
||
### `asserts` | ||
|
||
Use it for any other assertions, that don't quite fit the other contexts. | ||
|
||
```ts | ||
/** | ||
* Asserts that the given condition is met, if not a `AssertionError` will be thrown. | ||
* @param condition the condition that must be `true` | ||
* @param message an optional message for the error | ||
* @see AssertionError | ||
*/ | ||
export function asserts( | ||
condition: boolean, | ||
message: string = 'Failed Assertion' | ||
): asserts condition; | ||
``` | ||
|
||
## Utils | ||
|
||
The following functions do help you write even more concise code. | ||
|
||
### `error` | ||
|
||
This function will always throw the given error and helps keeping code easy to read. | ||
|
||
```ts | ||
/** | ||
* Always throws an error of the given type with the given message. | ||
* It can come in handy when assigning values with a ternary operator or the null operators. | ||
* @param errorType an error class, defaults to `AssertionError` | ||
* @param message the error message | ||
* @see IllegalStateError | ||
* @example | ||
* function myFun(foo: string | null) { | ||
* const bar: string = foo ?? error(PreconditionError, 'Argument may not be null'); | ||
* const result = bar.length > 0 ? 'OK' : error(); | ||
* } | ||
*/ | ||
export function error<T>( | ||
errorType: new (...args: any[]) => Error = IllegalStateError, | ||
message?: string | ||
): T; | ||
``` | ||
|
||
### `isDefined` | ||
|
||
A common type guard, to check that a value is defined. | ||
Make sure to use [`strictNullChecks`](https://basarat.gitbook.io/typescript/intro/strictnullchecks). | ||
|
||
```ts | ||
/** | ||
* Type guard to check if the given value is not nullable. | ||
* @param value the given value | ||
* @example | ||
* const x: string | null = 'Hello'; | ||
* check(isDefined(x)); | ||
* x.toLowerCase(); // no error! | ||
*/ | ||
export function isDefined<T>(value: T): value is NonNullable<T>; | ||
``` | ||
|
||
### `useIf` | ||
|
||
A function that helps with validating and typing when assigning variables. | ||
|
||
```ts | ||
/** | ||
* Returns a function that will return the passed in value, if it passes the given predicate. | ||
* If not, the given contract will throw an error with the given message. | ||
* @param predicate the predicate that the value must pass | ||
* @param contract the contract for context | ||
* @param message the message for the contract | ||
* @example | ||
* function myFun(foo: string | null) { | ||
* const bar = useIf(isDefined)(foo); | ||
* } | ||
*/ | ||
export function useIf<T, O extends T = T>( | ||
predicate: (value: T | O) => value is O, | ||
contract: ( | ||
condition: boolean, | ||
message?: string | ||
) => asserts condition = requires, | ||
contractMessage?: string | ||
): (value: T) => O; | ||
``` | ||
|
||
## Errors | ||
|
||
The following error classes are included: | ||
|
||
- `PreconditionError` → An error thrown, if a precondition for a function or method is not met. | ||
- `IllegalStateError` → An error thrown, if an object is an illegal state. | ||
- `PostconditionError` → An error thrown, if a function or method could not fulfil a postcondition. | ||
- `AssertionError` → An error thrown, if an assertion has failed. |
Oops, something went wrong.