From 51381db7206be197aef28854aa1d7b9dce2e9e76 Mon Sep 17 00:00:00 2001 From: Maximillian Dornseif Date: Sat, 25 Dec 2021 12:02:53 +0100 Subject: [PATCH 1/5] feat: Throw into debugger on fail --- LICENSE | 2 +- README.md | 302 ++++++++------------------------------------------- package.json | 8 +- src/index.ts | 1 + 4 files changed, 54 insertions(+), 259 deletions(-) diff --git a/LICENSE b/LICENSE index 1ddf792..0353075 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Evan Louie +Copyright (c) 2020, 2021 Evan Louie and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 4d5279c..96544a6 100644 --- a/README.md +++ b/README.md @@ -1,264 +1,56 @@ # Assertate -> TypeScript 3.7 assertion helper library - -A minimal library exposing basic -[TypeScript 3.7 assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions) -helpers with the goal of providing the out of the box assertions that most -people need and have to rewrite for every project. - -## Requirements - -| Requirement | Version | Note | -| ----------- | --------- | ---- | -| TypeScript | `>=3.7.0` | \* | -| ECMA-262 | | \*\* | - -- \* This library will work both with vanilla JavaScript as well as lower - versions of TypeScript. Assertions will throw the expected Errors in all - environments, however the the TS compiler will only know the narrowed type if - an assertion passes in version `>=3.7.0`. -- \*\* This library offers helpers around - [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) - and - [`Symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). - These functions may not work on some browsers which do not yet support those - APIs. - -## Installation - -```sh -yarn add assertate -``` - -Or - -```sh -npm install assertate -``` - -## Examples - -### The Basics - -The library provides the of the box assertions and control flow type-predicates -functions that you will need to validate basic data and primitive types. - -```typescript -import { - assert, - assertIsNumber, - getAssertionMessage, - getType, - isNumber, - isString, - setAssertionMessage -} from "assertate"; - -//////////////////////////////////////////////////////////////////////////////// -// Control-flow with type-predicates -//------------------------------------------------------------------------------ -// This feature has been in TypeScript for a while and is the foundation for -// being able to write type assertions. -// Using the `is...` functions, we can do type narrowing of using control-flow -// operators and functions such as `if` and `.filter` -//////////////////////////////////////////////////////////////////////////////// -const stringA: unknown = "a"; -const stringB: unknown = "b"; -const numberA: unknown = 123; - -if (isNumber(numberA)) { - // compiler now knows that in this if block, `numberA` is a number - const numberAAsFixedPointZero = numberA.toFixed(0); -} - -const stringsAsUpper = [stringA, stringB, numberA] - .filter(isString) - .map(str => str.toUpperCase()); // compiler knows that it is mapping over strings - -//////////////////////////////////////////////////////////////////////////////// -// try/catch based assertions with Errors -//------------------------------------------------------------------------------ -// Use the `assertIs...` and `assert` functions for Error/exception based -// assertions. -// These assertions use the `is...` type-predicates under the hood and are -// useful for allowing try/catch based control flow. -//////////////////////////////////////////////////////////////////////////////// -try { - const someUndefinedVar: unknown = undefined; - assertIsNumber(someUndefinedVar); // will throw an Error -} catch (err) { - console.error(err); // An assertion Error will be logged -} - -const someNumber: unknown = 123.456; -assertIsNumber(someNumber); // will not throw -// compiler now knows `someNumber` is a number and has all instance methods that a number has -const asFixedPointZero = someNumber.toFixed(0); - -//////////////////////////////////////////////////////////////////////////////// -// General assertion -//------------------------------------------------------------------------------ -// use the `assert` function if you need to write or compose more custom -// assertions -//////////////////////////////////////////////////////////////////////////////// -const someNumberOrString: unknown = "123"; - -// you can compose the `is` assertions using `assert` -assert(isNumber(someNumberOrString) || isString(someNumberOrString)); -// compiler now knows someNumberOrString is of type `number | string` - -// you can write assertions using standard type-guards using `assert` as well -assert(typeof someNumberOrString === "string"); -// compiler now knows that someNumberOrString is a string -const someNumberOrStringChars = someNumberOrString.split(","); - -//////////////////////////////////////////////////////////////////////////////// -// Set your own assertion messages -//------------------------------------------------------------------------------ -// A default assertion message is provided; will most likely be good enough for -// most cases -//////////////////////////////////////////////////////////////////////////////// -const defaultAssertionMessage = getAssertionMessage(); -setAssertionMessage( - (someValue, expectedType) => - `Im your message! Expected a ${expectedType}, got a ${getType(someValue)}` -); -try { - const aNumber: unknown = 123; - assertIsString(aNumber); -} catch (err) { - console.log(err); // An error with message 'Im your message! Expected a string, got a number' will be logged -} -``` - -### Complex Example -- Assertions on your domain data - -When interacting with data over the wire, its important that we validate the -data we receive is valid to what our domain logic expects. With assertions, we -can safely validate the data we receive both matches the expected types, but -also add custom domain logic to validate that the data has the a valid value. - -```typescript -import { isObject, isString, isNumber, assert } from "assertate"; - -//////////////////////////////////////////////////////////////////////////////// -// In this example we our going to write a Wizard validator. -// A Wizard is just a human that can do magic! -//////////////////////////////////////////////////////////////////////////////// -type Human = { - name: string; - age: number; -}; - -type Magical = { - canDoMagic: true; -}; - -type Wizard = Human & Magical; - -//////////////////////////////////////////////////////////////////////////////// -// Now lets write some type-predicates to validate our domain logic -//////////////////////////////////////////////////////////////////////////////// -/** - * Determines if the `value` is a valid Human - * - Has some non-empty string name - * - Age is greater than 0 - */ -function isHuman(value: unknown): value is Human { - // we can now validate the data in the object matches our expected types - if (isObject(value) && isString(value?.name) && isNumber(value?.age)) { - const { name, age } = value; // compiler knows `name` is a string and `age` is a number - // we can do our own custom validations against the type-checked data as well - // humans must be older than 0 and have a non-empty string name - if (age > 0 && name.length > 0) { - return true; +> TypeScript 3.7 assertion helper library which debugging helpers + +This is Evan Louie's most excellent [assertate](https://www.npmjs.com/package/assertate) library repacked with tiny debugging helpers. + +Assertate is a minimal library exposing basic +[TypeScript 3.7 assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions) helpers with the goal of providing a combination of compile time type assertions and run time assertions. + +In the `assertate-debug` variant you will be thrown into a debugger if an assentation fails. In most Javascript Implementation this will only happen if the code is actually executed in a debugger. If not, the assert will fail as usual. + +This works very nice with The VScode debugger and a "run Jest" configuration. [Add a debugger Configuration](http://f.foxel.org/Screen-Shot-2021-12-24-15-08-31.png) like this: + +```js +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Jest All", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": [ + "--runInBand" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "disableOptimisticBPs": true, + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + } + }, + { + "type": "node", + "request": "launch", + "name": "Jest Current File", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": [ + "${fileBasenameNoExtension}", + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "disableOptimisticBPs": true, + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + } } - } - return false; -} - -/** - * Determines if the `value` has a magical property - */ -function isMagical(value: unknown): value is Magical { - return isObject(value) && value?.magical === true; -} - -/** - * Determines if the `value` is a Wizard - * - is a human - * - is magical - */ -function isWizard(value: unknown): value is Wizard { - return isHuman(value) && isMagical(value); -} - -//////////////////////////////////////////////////////////////////////////////// -// Lets write some type assertions that use the type-predicates we wrote -//////////////////////////////////////////////////////////////////////////////// -/** - * Asserts that `value` is a Human object - */ -function assertIsHuman(value: unknown): asserts value is Human { - assert(isHuman(value)); -} - -/** - * Asserts that `value` is a Magical object - */ -function assertIsMagical(value: unknown): asserts value is Magical { - assert(isMagical(value)); -} - -/** - * Asserts that `value` is a Wizard - */ -function assertIsWizard(value: unknown): asserts value is Wizard { - assert(isWizard(value)); -} - -//////////////////////////////////////////////////////////////////////////////// -// Now imagine the following two humans are fetched from over the wire and that -// we know absolutely nothing about the data received. -// For the sake of the example, we know that they both have the 3 valid -// properties needed to be type-checked; but when receiving them from over -// a REST endpoint or some random JSON file, we would know absolutely nothing. -// The data retrieved could be a list, an object, a number, a string, anything. -// -// With the `assertIs...` functions we wrote, we can validate that: -// - the data received is in fact an object -// - the object has the expected keys -// - the values of the keys conform to our expected domain logic for Human, -// Magical, and Wizard -//////////////////////////////////////////////////////////////////////////////// -// Dr. Strange? He's magic! -const stephenStrange: unknown = { - name: "Stephen Strange", - age: 90, // born 1930 - canDoMagic: true -}; - -// Iron Man? sadly not -const tonyStark: unknown = { - name: "Tony Stark", - age: 50, // born 1970 - canDoMagic: false -}; - -assertIsHuman(stephenStrange); // no Error thrown -assertIsHuman(tonyStark); // no Error thrown -// compiler now knows that both people are humans -for (const person of [stephenStrange, tonyStark]) { - try { - assertIsMagical(person); // no Error thrown for stephenStrange, Error thrown for tonyStark - assertIsWizard(person); // no Error thrown for stephenStrange, code won't be reached for tonyStark - } catch (err) { - console.error(`${person.name} isn't a wizard :(`); // we can access `person.name` because the compiler knows the person is a human - } + ] } ``` +For further information check the [original documentation](https://www.npmjs.com/package/assertate). + ## API Swing by the [docs](https://evanlouie.github.io/assertate/) to get a full look diff --git a/package.json b/package.json index 1f25916..5aeb2b8 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,14 @@ "assertion", "isomorphic", "client", - "server" + "server", + "debug" ], "main": "./dist/index.js", "types": "./dist/index.d.ts", - "author": "Evan Louie ", - "homepage": "https://github.com/evanlouie/assertate/", + "author": "Maximillian Dornseif ", + "homepage": "https://github.com/mdornseif/assertate-debug/", + "repository": "https://github.com/mdornseif/assertate-debug", "license": "MIT", "scripts": { "generate-docs": "shx rm -rf docs && typedoc --out docs src && shx touch docs/.nojekyll", diff --git a/src/index.ts b/src/index.ts index 2e8d7df..9041bbc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -384,6 +384,7 @@ export function assert( message?: string ): asserts condition { if (!condition) { + debugger throw Error(message); } } From 869d0b8750cb7491f0b98246f3a30647081dac23 Mon Sep 17 00:00:00 2001 From: Maximillian Dornseif Date: Sat, 25 Dec 2021 12:10:23 +0100 Subject: [PATCH 2/5] chore: change package name --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5aeb2b8..084a104 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "assertate", + "name": "assertate-debug", "version": "2.4.0", "description": "TypeScript assertion helpers", "keywords": [ From a91e54dd1e1a3bb68d432d4e23151078d6068c5e Mon Sep 17 00:00:00 2001 From: Maximillian Dornseif Date: Sun, 26 Dec 2021 16:13:34 +0100 Subject: [PATCH 3/5] fix: assertIsDefined --- src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 2e8d7df..80e8408 100644 --- a/src/index.ts +++ b/src/index.ts @@ -362,11 +362,11 @@ export function assertIsNotUndefined( * @param additionalMessage further information on failure * @throws {Error} */ -export function assertIsDefined( - value: unknown, +export function assertIsDefined( + value: T, variableName?: string, additionalMessage?: string -): asserts value is NonNullable { +): asserts value is NonNullable { assert( isDefined(value), AssertionMessage(value, "defined", variableName, additionalMessage) From c8ab80f98c3fec8f2f54201d70c7cbe335bb0b80 Mon Sep 17 00:00:00 2001 From: Maximillian Dornseif Date: Sun, 26 Dec 2021 16:17:09 +0100 Subject: [PATCH 4/5] log --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 96544a6..908293b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This is Evan Louie's most excellent [assertate](https://www.npmjs.com/package/as Assertate is a minimal library exposing basic [TypeScript 3.7 assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions) helpers with the goal of providing a combination of compile time type assertions and run time assertions. -In the `assertate-debug` variant you will be thrown into a debugger if an assentation fails. In most Javascript Implementation this will only happen if the code is actually executed in a debugger. If not, the assert will fail as usual. +In the `assertate-debug` variant you will be thrown into a debugger if an assentation fails. In most Javascript Engine implementations, this will only happen if the code is actually executed in a debugger. If not, the assert will fail as usual. This works very nice with The VScode debugger and a "run Jest" configuration. [Add a debugger Configuration](http://f.foxel.org/Screen-Shot-2021-12-24-15-08-31.png) like this: diff --git a/package.json b/package.json index 084a104..0ad2efc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "assertate-debug", - "version": "2.4.0", + "version": "2.4.1", "description": "TypeScript assertion helpers", "keywords": [ "TypeScript", From d8037c40c3b5d42700e87dc3895fda1ba6e9ec3b Mon Sep 17 00:00:00 2001 From: Maximillian Dornseif Date: Sun, 26 Dec 2021 16:21:54 +0100 Subject: [PATCH 5/5] fix build --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ad2efc..6461966 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "assertate-debug", - "version": "2.4.1", + "version": "2.4.2", "description": "TypeScript assertion helpers", "keywords": [ "TypeScript",