diff --git a/.eslintrc.json b/.eslintrc.json index e1c7bc9..f4103e6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -278,7 +278,7 @@ "object-shorthand": "warn", "prefer-arrow-callback": "warn", "prefer-const": "warn", - "prefer-destructuring": ["warn", { "object": true, "array": false } ], + "prefer-destructuring": "off", // Annoying "prefer-numeric-literals": "warn", "prefer-rest-params": "error", "prefer-spread": "error", diff --git a/.vscode/launch.json b/.vscode/launch.json index c7f9b7c..c4730e4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -32,6 +32,15 @@ "windows": { "program": "${workspaceFolder}/node_modules/jest/bin/jest", } + }, + { + "type": "node", + "request": "attach", + "name": "Attach", + "port": 9229, + "skipFiles": [ + "/**" + ] } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 11d59e6..0a5dca2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,6 @@ "typescript.tsdk": "node_modules/typescript/lib", "editor.codeActionsOnSave": { "source.fixAll.eslint": true - } + }, + "debug.node.autoAttach": "on" } diff --git a/README.md b/README.md index 17498c8..a5c1c3c 100644 --- a/README.md +++ b/README.md @@ -1 +1,329 @@ -TODO: \ No newline at end of file +# Guardian + +Guardian is a collection of composable TypeScript/JavaScript type guards and assertions. Assertions use the new [assertion function](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions) types in TypeScript 3.7. + +## Table of Contents + + + +- [Guards](#guards) + * [Prebuilt Guards](#prebuilt-guards) + * [Arrays](#arrays) + * [Object Properties](#object-properties) + * [Composing Guards](#composing-guards) +- [Assertions](#assertions) + * [Value Assertions](#value-assertions) + * [Assert Property Decorator](#assert-property-decorator) + * [Assert Unreachable](#assert-unreachable) +- [Guard Reference](#guard-reference) + + + +## Guards + +A [type guard](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types) in TypeScript is a function that checks the type of a value. This is a function of the form: +```ts +function (value: unknown): value is Foo { + // Perform logic to determine if the value is indeed a 'Foo' and return true if it is, false otherwise. +} +``` +Guardian exposes the [[Guard]] type to capture this. It also exposes a more general [[Validator]] type, which is just a function that takes an unknown value and returns a boolean. + +### Prebuilt Guards + +Guardian includes a large number of prebuilt guards. A full list can be found in the [Guard Reference](#guard-reference). An example: +```ts +import { isDate } from 'guardian'; + +function doSomething(value: unknown) { + if (isDate(value)) { + // Inside this block value has type 'Date' + // ... + } +} +``` + +Some Guardian functions are factories that require specifying an additional value in order to generate a guard. For example: +```ts +import { isStringContaining } from 'guardian'; + +function doSomething(value: unknown) { + if (isStringContaining('foo')(value)) { + // Inside this block value has type 'string' + // We also know it contains the substring 'foo' but that isn't known in the type system. + // ... + } +} + +// You can also save the guard to call for later +const isStringContainingFoo = isStringContaining('foo'); +function doSomethingElse(value: unknown) { + if (isStringContainingFoo(value)) { + // ... + } +} +``` + +Some guards return tagged primitive types. These are primitive types that are combined with a special tag type that can restrict usage. For example, the [[isInteger]] guard will type a variable as an [[Integer]], which is just a number with an additional tag indicating that we know it is an integer. +```ts +import { isInteger, Integer } from 'guardian' + +function doSomethingWithNumber(value: number) { + // ... +} + +function doSomethingWithInteger(value: Integer) { + // ... +} + +const foo: number = 5; // Type is 'number' +doSomethingWithNumber(foo); // OK +doSomethingWithInteger(foo); // Error! Argument of type 'number' is not assignable to parameter of type 'Integer'. + +if (isInteger(foo)) { // Type is 'Integer' + doSomethingWithNumber(foo); // Still OK as 'Integer' is also a 'number' + doSomethingWithInteger(foo); // OK +} +``` + +### Arrays +Items in arrays can also be checked using the [[isEach]] guard factory. The guard will first check that the value is an array and will then check each item in the array against the specified guard. +```ts +import { isEach, isNumber } from 'guardian'; + +const isArrayOfNumbers = isEach(isNumber); + +function doSomething(value: unknown) { + if (isArrayOfNumbers(value)) { + // Inside this block value has type 'number[]' + // ... + } +} +``` + +### Object Properties +Similarly, you can check the properties of an object using the [[hasProperties]] guard factory: +```ts +import { hasDefinition, isString } from 'guardian'; + +const hasFooStringProperty = hasDefinition({ + foo: isString, +}); + +function doSomething(value: unknown) { + if (hasFooStringProperty(value)) { + // This checks if the value has the property 'foo' and that is a string, but it's still typed as 'unknown'. :( + // ... + } +} +``` + +In the previous example, the guard just asks as a validator. It doesn't inform the type system. However, the factory takes an optional generic argument that will set the type and check the keys of your validator object. The factory also takes an optional interface name parameter (used for assertions below). This makes it extremely easy to build strongly typed guards for your interfaces: +```ts +import { hasDefinition, isString } from 'guardian'; + +interface Foo { + foo: string; + bar: number[]; +} + +const isFoo = hasDefinition({ + foo: isString, + bar: isEach(isNumber), +}, 'Foo'); + +function doSomething(value: unknown) { + if (isFoo(value)) { + // Inside this block, value has type 'Foo' :) + // ... + } +} +``` +You can, of course, nest the guards however deep you'd like. + +### Composing Guards +Guardian comes with two operators ([[or]] and [[and]]) for easily combining guards together to create new guards. (In fact, many of the guards included in Guardian are created this way.) The [[or]] operator can be used to check if a value matches on of 2 or more guards. +```ts +import { isNumber, isString, isUndefined } from 'guardian'; + +function doSomething(value: unknown) { + if (or(isNumber, isString)(value)) { + // Inside this block value has type 'number|string' + // ... + } +} + +// You can also save the guard to call for later +const isStringOrUndefined = or(isString, isUndefined); +function doSomethingElse(value: unknown) { + if (isStringOrUndefined(value)) { + // Inside this block value has type 'string|undefined' + // ... + } +} +``` + +Simlarly, the [[and]] operator creates an intersection of types. +```ts +import { hasProperties, isBoolean, isString } from 'guardian'; + +interface Foo { + foo: boolean; +} +const isFoo = hasProperties({ + foo: isBoolean, +}, 'Foo'); + +interface Bar { + bar: number; +} +const isBar = hasProperties({ + bar: isNumber, +}, 'Bar'); + +const isFooAndBar = and(isFoo, isBar); + +function doSomething(value: unknown) { + if (isFooAndBar(value)) { + // Inside this block value has type 'Foo & Bar' so you can access 'value.foo' and 'value.bar' + } +} +``` + +## Assertions + +TypeScript introduced type support for [assertion functions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions) in release 3.7. Guardian includes the [[assert]] function to handle arbitrary assertion conditions. If the condition is met, execution continues, if not an [[AssertionError]] is thrown. Starting with TypeScript 3.7, the type engine also knows how to restrict the types after these assertions. +```ts +import { assert } from 'guardian'; + +function doSomething(value: unknown) { + // The condition argument here is whatever you want. + assert(typeof value === 'string'); + + // Now the type of value is 'string' + // If the value was not a string an error would be thrown. +} +``` +You can also specify a custom error message if you want to control the error message. + +### Value Assertions + +Guardian also includes an [[assertValue]] function if you want to make assertions about the value of a variable. This form of the assertion makes it each to throw meaningful error messages. The first value of the function is any [[Guard]] or [[Validator]], the second is the value to test, and the 3rd is an optional name for the variable. +```ts +import { assertValue, isString } from 'guardian'; + +const myVar: number = 5; +assertValue(isString, myVar, 'myVar'); // throws AssertionError: Expected 'myVar' to be a string but received: 5 +``` + +This works for your custom guards as well: +```ts +import { assertValue } from 'guardian'; + +function isFoo(value; unknown): value is Foo { + // Implement here +} + +const myVar: number = 5; +assertValue(isFoo, myVar); // throws AssertionError: Expected value to match 'isFoo' but received: 5 +``` +In this example the error message isn't very friendly. You can make the message more friendly by using the optional 4th parameter. This parameter is an expectation message and it is a sentence fragment that follows `'expected variable to '`: +```ts +assertValue(isFoo, myVar, 'myVar', 'be a Foo'); // throws AssertionError: Expected 'myVar' to be a Foo but received: 5 +``` + +The downside of this approach is that you need to specify the expectation each time you make a value assertion. There is a better alternative approach. The [[Guard]] and [[Validator]] type includes an additional optional `expectation` property that allows you to attach the expectation directly to the function. To use it: +```ts +(isFoo as Guard).expectation = 'be a Foo'; + +// From now on: +assertValue(isFoo, myVar); // throws AssertionError: Expected value to be a Foo but received: 5 +``` +Most of the prebuild guards in Guardian use this property to set more helpful messages, which is why the example with [[isString]] above looked good. + +### Assert Property Decorator + +Guardian also includes an [[Assert]] decorator that can be used on class properties. This decorator converts a property to a getter/setter with a setter that calls [[assertValue]]. For example: +```ts +class MyClass { + @Assert(or(isEmail, isUndefined)) + public email?: string; +} + +const instance = new MyClass(); +instance.email = 'foo'; // throws AssertionError: Expected 'MyClass#email' to be an email address or to be undefined but received: foo +``` +The decorator has the same signature as [[assertValue]] so the name and expectation can be overridden. The decorator also supports static properties. + +### Assert Unreachable + +Finally, Guardian includes an [[assertUnreachable]] function that will always throw if called. This is useful for protecting code when called from JavaScript (i.e. no type checking) or for ensuring fully discriminated unions to be safe from refactoring. +```ts +interface A { + type: 'a'; +} + +interface B { + type: 'b'; +} + +type All = A | B; + +function doSomething(arg: All): string { + if (arg.type === 'a') { + return 'A'; + } + + if (arg.type === 'b') { + return 'B'; + } + + // This could be reached if called from JS or if another type is added to 'All' + assertUnreachable(); // throws AssertionError: Statement should not be reachable +} +``` + +## Guard Reference + + +| Name | Factory Params | Return Type | Description | +| --- | --- | --- | --- | +| [[hasProperties]] | __validators__: PropertyValidators - The property validators
__interfaceName__: string - An optional interface name to report in the error message | T | Creates a guard that tests if a value is an object with properties matching the specified property validators +| [[isArray]] | | unknown[] | Guard that tests if the value is an array +| [[isBigInt]] | | bigint | Guard that tests if the value is an big integer +| [[isBoolean]] | | boolean | Guard that tests if the value is a boolean +| [[isComparable]] | | [[Comparable]] | Guard that tests if the value implements the [[Comparable]] interface +| [[isDate]] | | Date | Guard that tests if the value is an instance of Date +| [[isDeepEqualTo]] | __other__: T - The object to compare values to
__strict__: boolean - True to use strict equality (default) and false to use coercive equality | T | Creates a guard that tests if a value is equal to the specified value using the [deep-equal package]{@link https://www.npmjs.com/package/deep-equal} +| [[isDefined]] | | NonNullable | Guard that tests if the value is not null and not undefined +| [[isEach]] | __eachGuard__: Guard - The guard used for each item in the array. | T[] | Creates a guard that tests if a value is an array and that each value in the array satisfies the given guard. +| [[isEmail]] | | [[Email]] | Guard that tests if the value is an email address +| [[isEmptyArray]] | | unknown[] | Guard that tests if the value is an empty array +| [[isEmptyString]] | | "" | Guard that tests if the value is an empty string +| [[isEqualTo]] | __other__: T - The object to compare values to | T | Creates a guard that tests if a value is equal to a specified object. Values are compared by identity first andthen by using the [[Equatable]] or [[Comparable]] interfaces, if implemented. +| [[isEquatable]] | | [[Equatable]] | Guard that tests if the value implements the [[Equatable]] interface +| [[isFunction]] | | function | Guard that tests if the value is a function +| [[isGreaterThan]] | __other__: T - The value to compare to | T | Creates a guard that tests if a value if greater than a specified value. Will firstcompare using the [[Comparable]] interface, if implemented and will fall back to operator comparison. +| [[isGreaterThanOrEqualTo]] | __other__: T - The value to compare to | T | Creates a guard that tests if a value if greater than or equal to a specified value. Will firstcompare using the [[Comparable]] interface, if implemented and will fall back to operator comparison.Note that objects not implementing [[Comparable]] or custom value representations may return unexpectedresults as JS will revert to comparing string representations. +| [[isIdenticalTo]] | __other__: T - The other value to compare to | T | Creates a guard that tests if a value is identical to another value. This uses the JSstrict equality comparison operator (===) so primitives are compared by value but objectsare compared by reference. +| [[isInstanceOf]] | __constructor__: Constructor - The constructor | T | Creates a guard that tests if a value is an instance of the specified constructor +| [[isInteger]] | | [[Integer]] | Guard that tests if the value is an integer +| [[isLessThan]] | __other__: T - The value to compare to | T | Creates a guard that tests if a value if less than a specified value. Will firstcompare using the [[Comparable]] interface, if implemented and will fall back to operator comparison. +| [[isLessThanOrEqualTo]] | __other__: T - The value to compare to | T | Creates a guard that tests if a value if less than or equal to a specified value. Will firstcompare using the [[Comparable]] interface, if implemented and will fall back to operator comparison.Note that objects not implementing [[Comparable]] or custom value representations may return unexpectedresults as JS will revert to comparing string representations. +| [[isMatch]] | __regexp__: RegExp - The regular expression | string | Creates a guard that tests if a value is a string that matches the specified RegExp +| [[isNegative]] | | [[Negative]] | Guard that tests if the value is a negative number +| [[isNegativeInteger]] | | [[NegativeInteger]] | Guard that tests if the value is a negative integer +| [[isNil]] | | unknown | Guard that tests if the value is null or undefined +| [[isNonEmptyArray]] | | unknown[] | Guard that tests if the value is an array that is not empty +| [[isNonEmptyString]] | | string | Guard that tests if the value is a string that is not empty +| [[isNull]] | | null | Guard that tests if the value is null +| [[isNumber]] | | number | Guard that tests if the value is a number +| [[isObject]] | | object | Guard that tests if the value is any object (and not null) +| [[isPositive]] | | [[Positive]] | Guard that tests if the value is a positive number +| [[isPositiveInteger]] | | [[PositiveInteger]] | Guard that tests if the value is a positive integer +| [[isString]] | | string | Guard that tests if the value is a string +| [[isStringContaining]] | __substring__: string - The substring to check for | string | Creates a guard that tests if a value is a string containing the specified substring +| [[isStringNotContaining]] | __substring__: string - The substring to check for | string | Creates a guard that tests if a value is a string that does not contain the specified substring +| [[isSymbol]] | | symbol | Guard that tests if the value is a symbol +| [[isUndefined]] | | undefined | Guard that tests if the value is undefined + \ No newline at end of file diff --git a/docs/assets/js/search.js b/docs/assets/js/search.js index 9345cb6..d9ca66a 100644 --- a/docs/assets/js/search.js +++ b/docs/assets/js/search.js @@ -1,3 +1,3 @@ var typedoc = typedoc || {}; typedoc.search = typedoc.search || {}; - typedoc.search.data = {"kinds":{"65536":"Type literal"},"rows":[{"id":0,"kind":65536,"name":"__type","url":"globals.html#propertyvalidators.__type","classes":"tsd-kind-type-literal tsd-parent-kind-type-alias tsd-is-not-exported","parent":"PropertyValidators"},{"id":1,"kind":65536,"name":"__type","url":"globals.html#constructor.__type","classes":"tsd-kind-type-literal tsd-parent-kind-type-alias tsd-is-not-exported","parent":"Constructor"}]}; \ No newline at end of file + typedoc.search.data = {"kinds":{"65536":"Type literal"},"rows":[{"id":0,"kind":65536,"name":"__type","url":"globals.html#tag.__type","classes":"tsd-kind-type-literal tsd-parent-kind-type-alias tsd-is-not-exported","parent":"Tag"},{"id":1,"kind":65536,"name":"__type","url":"globals.html#propertyvalidators.__type","classes":"tsd-kind-type-literal tsd-parent-kind-type-alias tsd-is-not-exported","parent":"PropertyValidators"},{"id":2,"kind":65536,"name":"__type","url":"globals.html#constructor.__type","classes":"tsd-kind-type-literal tsd-parent-kind-type-alias tsd-is-not-exported","parent":"Constructor"}]}; \ No newline at end of file diff --git a/docs/classes/assertionerror.html b/docs/classes/assertionerror.html index 6980eb9..93a39ef 100644 --- a/docs/classes/assertionerror.html +++ b/docs/classes/assertionerror.html @@ -3,7 +3,7 @@ - AssertionError | guardian + AssertionError | Guardian Documentation @@ -22,7 +22,7 @@
  • Preparing search index...
  • The search index is not available
  • - guardian + Guardian Documentation
    diff --git a/docs/globals.html b/docs/globals.html index e5c81d7..689d3a3 100644 --- a/docs/globals.html +++ b/docs/globals.html @@ -3,7 +3,7 @@ - guardian + Guardian Documentation @@ -22,7 +22,7 @@
  • Preparing search index...
  • The search index is not available
  • - guardian + Guardian Documentation
    @@ -56,7 +56,7 @@ Globals -

    guardian

    +

    Guardian Documentation

    @@ -77,12 +77,8 @@

    Classes

    Interfaces

    @@ -101,21 +97,11 @@

    Type aliases

  • Positive
  • PositiveInteger
  • PropertyValidators
  • +
  • Tag
  • +
  • Tagged
  • Validator
  • -
    -

    Variables

    - -

    Functions

    -
    - -

    Validator

    -
    Validator: function & HasExpectation
    - -
    -
    -

    A function that performs a validation on an unknow value and returns a boolean.

    -
    -
    -
    - -
    -

    Variables

    -
    - -

    Const isComparable

    -
    isComparable: function & HasExpectation = hasProperties<Comparable>({compareTo: isFunction,}, 'Comparable')
    - -
    -
    -

    Guard that tests if the value implements the Comparable interface

    -
    -
    -
    param
    -

    The value to test

    -
    -
    returns
    -

    The result of the test

    -
    -
    -
    -
    -
    - -

    Const isDate

    -
    isDate: function & HasExpectation = isInstanceOf(Date)
    - -
    -
    -

    Guard that tests if the value is an instance of Date

    -
    -
    -
    param
    -

    The value to test

    -
    -
    returns
    -

    The result of the test

    -
    -
    -
    -
    -
    - -

    Const isEmptyString

    -
    isEmptyString: function & HasExpectation = isIdenticalTo('')
    - -
    -
    -

    Guard that tests if the value is an empty string

    -
    -
    -
    param
    -

    The value to test

    -
    -
    returns
    -

    The result of the test

    -
    -
    -
    -
    -
    - -

    Const isEquatable

    -
    isEquatable: function & HasExpectation = hasProperties<Equatable>({equals: isFunction,}, 'Equatable')
    +
    + +

    Tag

    +
    Tag<T>: object
    -

    Guard that tests if the value implements the Equatable interface

    +

    A type tag.

    -
    -
    param
    -

    The value to test

    -
    -
    returns
    -

    The result of the test

    -
    -
    -
    -
    - -

    Const isNegativeInteger

    -
    isNegativeInteger: Guard<NegativeInteger> = and(isNegative, isInteger)
    - -
    -
    -

    Guard that tests if the value is a negative integer

    -
    -
    -
    param
    -

    The value to test

    -
    -
    returns
    -

    The result of the test

    -
    -
    -
    - -

    Const isNil

    -
    isNil: function & HasExpectation = or(isNull, isUndefined)
    +
    + +

    Tagged

    +
    Tagged<TValue, TTag>: TValue & Tag<TTag>
    -

    Guard that tests if the value is null or undefined

    +

    A tagged type.

    -
    -
    param
    -

    The value to test

    -
    -
    returns
    -

    The result of the test

    -
    -
    +

    Type parameters

    +
      +
    • +

      TValue

      +
    • +
    • +

      TTag: string

      +
    • +
    -
    - -

    Const isPositiveInteger

    -
    isPositiveInteger: Guard<PositiveInteger> = and(isPositive, isInteger)
    +
    + +

    Validator

    +
    Validator: function & HasExpectation
    -

    Guard that tests if the value is a positive integer

    +

    A function that performs a validation on an unknow value and returns a boolean.

    -
    -
    param
    -

    The value to test

    -
    -
    returns
    -

    The result of the test

    -
    -
    @@ -596,7 +475,7 @@

    Assert

  • @@ -644,7 +523,7 @@

    and

  • @@ -682,7 +561,7 @@

    Returns
    @@ -729,7 +608,7 @@

    Returns
    @@ -785,7 +664,7 @@

    Returns
    @@ -850,7 +729,7 @@

    Returns
    @@ -888,7 +767,7 @@

    assert

  • @@ -930,7 +809,7 @@

    assertUnreachable

  • @@ -963,7 +842,7 @@

    assertValue

  • @@ -1009,7 +888,7 @@

    Returns asserts
  • @@ -1058,7 +937,7 @@

    hasProperties

  • @@ -1102,7 +981,7 @@

    isArray

  • @@ -1128,13 +1007,13 @@

    Returns value

    isBigInt

      -
    • isBigInt(value: unknown): value is string
    • +
    • isBigInt(value: unknown): value is bigint
    -

    Returns value is string

    +

    Returns value is bigint

    The result of the test

  • @@ -1160,13 +1039,13 @@

    Returns value

    isBoolean

      -
    • isBoolean(value: unknown): value is string
    • +
    • isBoolean(value: unknown): value is boolean
    -

    Returns value is string

    +

    Returns value is boolean

    +

    The result of the test

    +

  • + +

  • +
    + +

    isComparable

    +
      +
    • isComparable(value: unknown): value is Comparable
    • +
    +
      +
    • + +
      +
      +

      Guard that tests if the value implements the Comparable interface

      +
      +
      +

      Parameters

      +
        +
      • +
        value: unknown
        +
        +

        The value to test

        +
        +
      • +
      +

      Returns value is Comparable

      +

      The result of the test

      +
    • +
    +
    +
    + +

    isDate

    +
      +
    • isDate(value: unknown): value is Date
    • +
    +
      +
    • + +
      +
      +

      Guard that tests if the value is an instance of Date

      +
      +
      +

      Parameters

      +
        +
      • +
        value: unknown
        +
        +

        The value to test

        +
        +
      • +
      +

      Returns value is Date

      The result of the test

    @@ -1198,7 +1141,7 @@

    isDeepEqualTo

  • @@ -1242,7 +1185,7 @@

    isDefined

  • @@ -1281,7 +1224,7 @@

    isEach

  • @@ -1310,7 +1253,7 @@

    Returns
    @@ -1342,7 +1285,7 @@

    isEmail

  • @@ -1374,7 +1317,7 @@

    isEmptyArray

  • @@ -1396,6 +1339,38 @@

    Returns value

  • +
    + +

    isEmptyString

    +
      +
    • isEmptyString(value: unknown): value is ""
    • +
    +
      +
    • + +
      +
      +

      Guard that tests if the value is an empty string

      +
      +
      +

      Parameters

      +
        +
      • +
        value: unknown
        +
        +

        The value to test

        +
        +
      • +
      +

      Returns value is ""

      +

      The result of the test

      +
    • +
    +

    isEqualTo

    @@ -1406,12 +1381,12 @@

    isEqualTo

  • -

    Creates a guard that tests if a value is equal to a specified objects. Objects are compared by identity first and +

    Creates a guard that tests if a value is equal to a specified object. Values are compared by identity first and then by using the Equatable or Comparable interfaces, if implemented.

    @@ -1435,6 +1410,38 @@

    Returns + +

    isEquatable

    +
      +
    • isEquatable(value: unknown): value is Equatable
    • +
    +
      +
    • + +
      +
      +

      Guard that tests if the value implements the Equatable interface

      +
      +
      +

      Parameters

      +
        +
      • +
        value: unknown
        +
        +

        The value to test

        +
        +
      • +
      +

      Returns value is Equatable

      +

      The result of the test

      +
    • +
    +
  • isFunction

    @@ -1445,7 +1452,7 @@

    isFunction

  • @@ -1477,7 +1484,7 @@

    isGreaterThan

  • @@ -1516,7 +1523,7 @@

    isGreaterThanOrEqualTo

  • @@ -1557,9 +1564,16 @@

    isIdenticalTo

  • +
    +
    +

    Creates a guard that tests if a value is identical to another value. This uses the JS + strict equality comparison operator (===) so primitives are compared by value but objects + are compared by reference.

    +
    +

    Type parameters

    • @@ -1570,9 +1584,13 @@

      Parameters

      • other: T
        +
        +

        The other value to compare to

        +

      Returns Guard<T>

      +

      The guard

  • @@ -1586,7 +1604,7 @@

    isInstanceOf

  • @@ -1624,7 +1642,7 @@

    isInteger

  • @@ -1656,7 +1674,7 @@

    isLessThan

  • @@ -1695,7 +1713,7 @@

    isLessThanOrEqualTo

  • @@ -1736,7 +1754,7 @@

    isMatch

  • @@ -1768,7 +1786,7 @@

    isNegative

  • @@ -1790,6 +1808,70 @@

    Returns value

  • +
    + +

    isNegativeInteger

    +
      +
    • isNegativeInteger(value: unknown): value is NegativeInteger
    • +
    +
      +
    • + +
      +
      +

      Guard that tests if the value is a negative integer

      +
      +
      +

      Parameters

      +
        +
      • +
        value: unknown
        +
        +

        The value to test

        +
        +
      • +
      +

      Returns value is NegativeInteger

      +

      The result of the test

      +
    • +
    +
    +
    + +

    isNil

    +
      +
    • isNil(value: unknown): value is unknown
    • +
    +
      +
    • + +
      +
      +

      Guard that tests if the value is null or undefined

      +
      +
      +

      Parameters

      +
        +
      • +
        value: unknown
        +
        +

        The value to test

        +
        +
      • +
      +

      Returns value is unknown

      +

      The result of the test

      +
    • +
    +

    isNonEmptyArray

    @@ -1800,7 +1882,7 @@

    isNonEmptyArray

  • @@ -1832,7 +1914,7 @@

    isNonEmptyString

  • @@ -1864,7 +1946,7 @@

    isNull

  • @@ -1896,7 +1978,7 @@

    isNumber

  • @@ -1928,7 +2010,7 @@

    isObject

  • @@ -1960,7 +2042,7 @@

    isPositive

  • @@ -1982,6 +2064,38 @@

    Returns value

  • +
    + +

    isPositiveInteger

    +
      +
    • isPositiveInteger(value: unknown): value is PositiveInteger
    • +
    +
      +
    • + +
      +
      +

      Guard that tests if the value is a positive integer

      +
      +
      +

      Parameters

      +
        +
      • +
        value: unknown
        +
        +

        The value to test

        +
        +
      • +
      +

      Returns value is PositiveInteger

      +

      The result of the test

      +
    • +
    +

    isString

    @@ -1992,7 +2106,7 @@

    isString

  • @@ -2024,7 +2138,7 @@

    isStringContaining

  • @@ -2056,7 +2170,7 @@

    isStringNotContaining

  • @@ -2082,13 +2196,13 @@

    Returns

    isSymbol

      -
    • isSymbol(value: unknown): value is string
    • +
    • isSymbol(value: unknown): value is symbol
    -

    Returns value is string

    +

    Returns value is symbol

    The result of the test

  • @@ -2120,7 +2234,7 @@

    isUndefined

  • @@ -2156,7 +2270,7 @@

    or

  • @@ -2194,7 +2308,7 @@

    Returns
    @@ -2241,7 +2355,7 @@

    Returns
    @@ -2297,7 +2411,7 @@

    Returns
    @@ -2362,7 +2476,7 @@

    Returns
    @@ -2408,24 +2522,12 @@

    Returns Comparable

  • -
  • - EmailBrand -
  • Equatable
  • HasExpectation
  • -
  • - IntegerBrand -
  • -
  • - NegativeBrand -
  • -
  • - PositiveBrand -
  • ComparisonResult
  • @@ -2465,29 +2567,14 @@

    Returns PropertyValidators -
  • - Validator -
  • -
  • - isComparable -
  • -
  • - isDate -
  • -
  • - isEmptyString -
  • -
  • - isEquatable -
  • -
  • - isNegativeInteger +
  • + Tag
  • -
  • - isNil +
  • + Tagged
  • -
  • - isPositiveInteger +
  • + Validator
  • Assert @@ -2516,6 +2603,12 @@

    Returns isBoolean

  • +
  • + isComparable +
  • +
  • + isDate +
  • isDeepEqualTo
  • @@ -2531,9 +2624,15 @@

    Returns isEmptyArray +
  • + isEmptyString +
  • isEqualTo
  • +
  • + isEquatable +
  • isFunction
  • @@ -2564,6 +2663,12 @@

    Returns isNegative +
  • + isNegativeInteger +
  • +
  • + isNil +
  • isNonEmptyArray
  • @@ -2582,6 +2687,9 @@

    Returns isPositive +
  • + isPositiveInteger +
  • isString
  • diff --git a/docs/index.html b/docs/index.html index 800f8e8..238614b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,7 +3,7 @@ - guardian + Guardian Documentation @@ -22,7 +22,7 @@
  • Preparing search index...
  • The search index is not available
  • - guardian + Guardian Documentation
    @@ -56,7 +56,7 @@ Globals -

    guardian

    +

    Guardian Documentation

    @@ -64,7 +64,508 @@

    guardian

    -

    TODO:

    + +

    Guardian

    +
    +

    Guardian is a collection of composable TypeScript/JavaScript type guards and assertions. Assertions use the new assertion function types in TypeScript 3.7.

    + +

    Table of Contents

    +
    + + + + +

    Guards

    +
    +

    A type guard in TypeScript is a function that checks the type of a value. This is a function of the form:

    +
    function (value: unknown): value is Foo {
    +    // Perform logic to determine if the value is indeed a 'Foo' and return true if it is, false otherwise.
    +}
    +

    Guardian exposes the Guard type to capture this. It also exposes a more general Validator type, which is just a function that takes an unknown value and returns a boolean.

    + +

    Prebuilt Guards

    +
    +

    Guardian includes a large number of prebuilt guards. A full list can be found in the Guard Reference. An example:

    +
    import { isDate } from 'guardian';
    +
    +function doSomething(value: unknown) {
    +    if (isDate(value)) {
    +        // Inside this block value has type 'Date'
    +        // ...
    +    }
    +}
    +

    Some Guardian functions are factories that require specifying an additional value in order to generate a guard. For example:

    +
    import { isStringContaining } from 'guardian';
    +
    +function doSomething(value: unknown) {
    +    if (isStringContaining('foo')(value)) {
    +        // Inside this block value has type 'string'
    +        // We also know it contains the substring 'foo' but that isn't known in the type system.
    +        // ...
    +    }
    +}
    +
    +// You can also save the guard to call for later
    +const isStringContainingFoo = isStringContaining('foo');
    +function doSomethingElse(value: unknown) {
    +    if (isStringContainingFoo(value)) {
    +        // ...
    +    }
    +}
    +

    Some guards return tagged primitive types. These are primitive types that are combined with a special tag type that can restrict usage. For example, the isInteger guard will type a variable as an Integer, which is just a number with an additional tag indicating that we know it is an integer.

    +
    import { isInteger, Integer } from 'guardian'
    +
    +function doSomethingWithNumber(value: number) {
    +    // ...
    +}
    +
    +function doSomethingWithInteger(value: Integer) {
    +    // ...
    +}
    +
    +const foo: number = 5; // Type is 'number'
    +doSomethingWithNumber(foo); // OK
    +doSomethingWithInteger(foo); // Error! Argument of type 'number' is not assignable to parameter of type 'Integer'.
    +
    +if (isInteger(foo)) { // Type is 'Integer'
    +    doSomethingWithNumber(foo); // Still OK as 'Integer' is also a 'number'
    +    doSomethingWithInteger(foo); // OK
    +}
    + +

    Arrays

    +
    +

    Items in arrays can also be checked using the isEach guard factory. The guard will first check that the value is an array and will then check each item in the array against the specified guard.

    +
    import { isEach, isNumber } from 'guardian';
    +
    +const isArrayOfNumbers = isEach(isNumber);
    +
    +function doSomething(value: unknown) {
    +    if (isArrayOfNumbers(value)) {
    +        // Inside this block value has type 'number[]'
    +        // ...
    +    }
    +}
    + +

    Object Properties

    +
    +

    Similarly, you can check the properties of an object using the hasProperties guard factory:

    +
    import { hasDefinition, isString } from 'guardian';
    +
    +const hasFooStringProperty = hasDefinition({
    +    foo: isString,
    +});
    +
    +function doSomething(value: unknown) {
    +    if (hasFooStringProperty(value)) {
    +        // This checks if the value has the property 'foo' and that is a string, but it's still typed as 'unknown'. :(
    +        // ...
    +    }
    +}
    +

    In the previous example, the guard just asks as a validator. It doesn't inform the type system. However, the factory takes an optional generic argument that will set the type and check the keys of your validator object. The factory also takes an optional interface name parameter (used for assertions below). This makes it extremely easy to build strongly typed guards for your interfaces:

    +
    import { hasDefinition, isString } from 'guardian';
    +
    +interface Foo {
    +    foo: string;
    +    bar: number[];
    +}
    +
    +const isFoo = hasDefinition<Foo>({
    +    foo: isString,
    +    bar: isEach(isNumber),
    +}, 'Foo');
    +
    +function doSomething(value: unknown) {
    +    if (isFoo(value)) {
    +        // Inside this block, value has type 'Foo' :)
    +        // ...
    +    }
    +}
    +

    You can, of course, nest the guards however deep you'd like.

    + +

    Composing Guards

    +
    +

    Guardian comes with two operators (or and and) for easily combining guards together to create new guards. (In fact, many of the guards included in Guardian are created this way.) The or operator can be used to check if a value matches on of 2 or more guards.

    +
    import { isNumber, isString, isUndefined } from 'guardian';
    +
    +function doSomething(value: unknown) {
    +    if (or(isNumber, isString)(value)) {
    +        // Inside this block value has type 'number|string'
    +        // ...
    +    }
    +}
    +
    +// You can also save the guard to call for later
    +const isStringOrUndefined = or(isString, isUndefined);
    +function doSomethingElse(value: unknown) {
    +    if (isStringOrUndefined(value)) {
    +        // Inside this block value has type 'string|undefined'
    +        // ...
    +    }
    +}
    +

    Simlarly, the and operator creates an intersection of types.

    +
    import { hasProperties, isBoolean, isString } from 'guardian';
    +
    +interface Foo {
    +    foo: boolean;
    +}
    +const isFoo = hasProperties<Foo>({
    +    foo: isBoolean,
    +}, 'Foo');
    +
    +interface Bar {
    +    bar: number;
    +}
    +const isBar = hasProperties<Bar>({
    +    bar: isNumber,
    +}, 'Bar');
    +
    +const isFooAndBar = and(isFoo, isBar);
    +
    +function doSomething(value: unknown) {
    +    if (isFooAndBar(value)) {
    +        // Inside this block value has type 'Foo & Bar' so you can access 'value.foo' and 'value.bar'
    +    }
    +}
    + +

    Assertions

    +
    +

    TypeScript introduced type support for assertion functions in release 3.7. Guardian includes the assert function to handle arbitrary assertion conditions. If the condition is met, execution continues, if not an AssertionError is thrown. Starting with TypeScript 3.7, the type engine also knows how to restrict the types after these assertions.

    +
    import { assert } from 'guardian';
    +
    +function doSomething(value: unknown) {
    +    // The condition argument here is whatever you want.
    +    assert(typeof value === 'string');
    +
    +    // Now the type of value is 'string'
    +    // If the value was not a string an error would be thrown.
    +}
    +

    You can also specify a custom error message if you want to control the error message.

    + +

    Value Assertions

    +
    +

    Guardian also includes an assertValue function if you want to make assertions about the value of a variable. This form of the assertion makes it each to throw meaningful error messages. The first value of the function is any Guard or Validator, the second is the value to test, and the 3rd is an optional name for the variable.

    +
    import { assertValue, isString } from 'guardian';
    +
    +const myVar: number = 5;
    +assertValue(isString, myVar, 'myVar'); // throws AssertionError: Expected 'myVar' to be a string but received: 5
    +

    This works for your custom guards as well:

    +
    import { assertValue } from 'guardian';
    +
    +function isFoo(value; unknown): value is Foo {
    +    // Implement here
    +} 
    +
    +const myVar: number = 5;
    +assertValue(isFoo, myVar); // throws AssertionError: Expected value to match 'isFoo' but received: 5
    +

    In this example the error message isn't very friendly. You can make the message more friendly by using the optional 4th parameter. This parameter is an expectation message and it is a sentence fragment that follows 'expected variable to ':

    +
    assertValue(isFoo, myVar, 'myVar', 'be a Foo'); // throws AssertionError: Expected 'myVar' to be a Foo but received: 5
    +

    The downside of this approach is that you need to specify the expectation each time you make a value assertion. There is a better alternative approach. The Guard and Validator type includes an additional optional expectation property that allows you to attach the expectation directly to the function. To use it:

    +
    (isFoo as Guard).expectation = 'be a Foo';
    +
    +// From now on:
    +assertValue(isFoo, myVar); // throws AssertionError: Expected value to be a Foo but received: 5
    +

    Most of the prebuild guards in Guardian use this property to set more helpful messages, which is why the example with isString above looked good.

    + +

    Assert Property Decorator

    +
    +

    Guardian also includes an Assert decorator that can be used on class properties. This decorator converts a property to a getter/setter with a setter that calls assertValue. For example:

    +
    class MyClass {
    +    @Assert(or(isEmail, isUndefined))
    +    public email?: string;
    +}
    +
    +const instance = new MyClass();
    +instance.email = 'foo'; // throws AssertionError: Expected 'MyClass#email' to be an email address or to be undefined but received: foo
    +

    The decorator has the same signature as assertValue so the name and expectation can be overridden. The decorator also supports static properties.

    + +

    Assert Unreachable

    +
    +

    Finally, Guardian includes an assertUnreachable function that will always throw if called. This is useful for protecting code when called from JavaScript (i.e. no type checking) or for ensuring fully discriminated unions to be safe from refactoring.

    +
    interface A {
    +    type: 'a';
    +}
    +
    +interface B {
    +    type: 'b';
    +}
    +
    +type All = A | B;
    +
    +function doSomething(arg: All): string {
    +    if (arg.type === 'a') {
    +        return 'A';
    +    }
    +
    +    if (arg.type === 'b') {
    +        return 'B';
    +    }
    +
    +    // This could be reached if called from JS or if another type is added to 'All'
    +    assertUnreachable(); // throws AssertionError: Statement should not be reachable
    +}
    + +

    Guard Reference

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameFactory ParamsReturn TypeDescription
    hasPropertiesvalidators: PropertyValidators - The property validators
    interfaceName: string - An optional interface name to report in the error message
    TCreates a guard that tests if a value is an object with properties matching the specified property validators
    isArrayunknown[]Guard that tests if the value is an array
    isBigIntbigintGuard that tests if the value is an big integer
    isBooleanbooleanGuard that tests if the value is a boolean
    isComparableComparableGuard that tests if the value implements the Comparable interface
    isDateDateGuard that tests if the value is an instance of Date
    isDeepEqualToother: T - The object to compare values to
    strict: boolean - True to use strict equality (default) and false to use coercive equality
    TCreates a guard that tests if a value is equal to the specified value using the deep-equal package
    isDefinedNonNullableGuard that tests if the value is not null and not undefined
    isEacheachGuard: Guard - The guard used for each item in the array.T[]Creates a guard that tests if a value is an array and that each value in the array satisfies the given guard.
    isEmailEmailGuard that tests if the value is an email address
    isEmptyArrayunknown[]Guard that tests if the value is an empty array
    isEmptyString""Guard that tests if the value is an empty string
    isEqualToother: T - The object to compare values toTCreates a guard that tests if a value is equal to a specified object. Values are compared by identity first andthen by using the Equatable or Comparable interfaces, if implemented.
    isEquatableEquatableGuard that tests if the value implements the Equatable interface
    isFunctionfunctionGuard that tests if the value is a function
    isGreaterThanother: T - The value to compare toTCreates a guard that tests if a value if greater than a specified value. Will firstcompare using the Comparable interface, if implemented and will fall back to operator comparison.
    isGreaterThanOrEqualToother: T - The value to compare toTCreates a guard that tests if a value if greater than or equal to a specified value. Will firstcompare using the Comparable interface, if implemented and will fall back to operator comparison.Note that objects not implementing Comparable or custom value representations may return unexpectedresults as JS will revert to comparing string representations.
    isIdenticalToother: T - The other value to compare toTCreates a guard that tests if a value is identical to another value. This uses the JSstrict equality comparison operator (===) so primitives are compared by value but objectsare compared by reference.
    isInstanceOfconstructor: Constructor - The constructorTCreates a guard that tests if a value is an instance of the specified constructor
    isIntegerIntegerGuard that tests if the value is an integer
    isLessThanother: T - The value to compare toTCreates a guard that tests if a value if less than a specified value. Will firstcompare using the Comparable interface, if implemented and will fall back to operator comparison.
    isLessThanOrEqualToother: T - The value to compare toTCreates a guard that tests if a value if less than or equal to a specified value. Will firstcompare using the Comparable interface, if implemented and will fall back to operator comparison.Note that objects not implementing Comparable or custom value representations may return unexpectedresults as JS will revert to comparing string representations.
    isMatchregexp: RegExp - The regular expressionstringCreates a guard that tests if a value is a string that matches the specified RegExp
    isNegativeNegativeGuard that tests if the value is a negative number
    isNegativeIntegerNegativeIntegerGuard that tests if the value is a negative integer
    isNilunknownGuard that tests if the value is null or undefined
    isNonEmptyArrayunknown[]Guard that tests if the value is an array that is not empty
    isNonEmptyStringstringGuard that tests if the value is a string that is not empty
    isNullnullGuard that tests if the value is null
    isNumbernumberGuard that tests if the value is a number
    isObjectobjectGuard that tests if the value is any object (and not null)
    isPositivePositiveGuard that tests if the value is a positive number
    isPositiveIntegerPositiveIntegerGuard that tests if the value is a positive integer
    isStringstringGuard that tests if the value is a string
    isStringContainingsubstring: string - The substring to check forstringCreates a guard that tests if a value is a string containing the specified substring
    isStringNotContainingsubstring: string - The substring to check forstringCreates a guard that tests if a value is a string that does not contain the specified substring
    isSymbolsymbolGuard that tests if the value is a symbol
    isUndefinedundefinedGuard that tests if the value is undefined
    @@ -106,7 +106,7 @@

    compareTo

  • diff --git a/docs/interfaces/emailbrand.html b/docs/interfaces/emailbrand.html deleted file mode 100644 index 710cf21..0000000 --- a/docs/interfaces/emailbrand.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - EmailBrand | guardian - - - - - -
    -
    -
    -
    - -
    -
    - Options -
    -
    - All -
      -
    • Public
    • -
    • Public/Protected
    • -
    • All
    • -
    -
    - - - - - - -
    -
    - Menu -
    -
    -
    -
    -
    -
    - -

    Interface EmailBrand

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Brand interface for Email

    -
    -
    -
    -
    -

    Hierarchy

    -
      -
    • - EmailBrand -
    • -
    -
    -
    -

    Index

    -
    -
    -
    -

    Properties

    - -
    -
    -
    -
    -
    -

    Properties

    -
    - -

    __Email__

    -
    __Email__: void
    - -
    -
    -
    - -
    -
    -
    -
    -

    Legend

    -
    -
      -
    • Module
    • -
    • Object literal
    • -
    • Variable
    • -
    • Function
    • -
    • Function with type parameter
    • -
    • Index signature
    • -
    • Type alias
    • -
    • Type alias with type parameter
    • -
    -
      -
    • Enumeration
    • -
    • Enumeration member
    • -
    • Property
    • -
    • Method
    • -
    -
      -
    • Interface
    • -
    • Interface with type parameter
    • -
    • Constructor
    • -
    • Property
    • -
    • Method
    • -
    • Index signature
    • -
    -
      -
    • Class
    • -
    • Class with type parameter
    • -
    • Constructor
    • -
    • Property
    • -
    • Method
    • -
    • Accessor
    • -
    • Index signature
    • -
    -
      -
    • Inherited constructor
    • -
    • Inherited property
    • -
    • Inherited method
    • -
    • Inherited accessor
    • -
    -
      -
    • Protected property
    • -
    • Protected method
    • -
    • Protected accessor
    • -
    -
      -
    • Private property
    • -
    • Private method
    • -
    • Private accessor
    • -
    -
      -
    • Static property
    • -
    • Static method
    • -
    -
    -
    -
    -
    -

    Generated using TypeDoc

    -
    -
    - - - - \ No newline at end of file diff --git a/docs/interfaces/equatable.html b/docs/interfaces/equatable.html index 28c7658..ea7757f 100644 --- a/docs/interfaces/equatable.html +++ b/docs/interfaces/equatable.html @@ -3,7 +3,7 @@ - Equatable | guardian + Equatable | Guardian Documentation @@ -22,7 +22,7 @@
  • Preparing search index...
  • The search index is not available
  • - guardian + Guardian Documentation
    @@ -106,7 +106,7 @@

    equals

  • diff --git a/docs/interfaces/hasexpectation.html b/docs/interfaces/hasexpectation.html index 4cdf661..802706c 100644 --- a/docs/interfaces/hasexpectation.html +++ b/docs/interfaces/hasexpectation.html @@ -3,7 +3,7 @@ - HasExpectation | guardian + HasExpectation | Guardian Documentation @@ -22,7 +22,7 @@
  • Preparing search index...
  • The search index is not available
  • - guardian + Guardian Documentation
    @@ -102,7 +102,7 @@

    Optional expectation

    expectation: undefined | string
    diff --git a/docs/interfaces/integerbrand.html b/docs/interfaces/integerbrand.html deleted file mode 100644 index abc7d21..0000000 --- a/docs/interfaces/integerbrand.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - IntegerBrand | guardian - - - - - -
    -
    -
    -
    - -
    -
    - Options -
    -
    - All -
      -
    • Public
    • -
    • Public/Protected
    • -
    • All
    • -
    -
    - - - - - - -
    -
    - Menu -
    -
    -
    -
    -
    -
    - -

    Interface IntegerBrand

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Brand interface for Integer

    -
    -
    -
    -
    -

    Hierarchy

    -
      -
    • - IntegerBrand -
    • -
    -
    -
    -

    Index

    -
    -
    -
    -

    Properties

    - -
    -
    -
    -
    -
    -

    Properties

    -
    - -

    __Integer__

    -
    __Integer__: void
    - -
    -
    -
    - -
    -
    -
    -
    -

    Legend

    -
    -
      -
    • Module
    • -
    • Object literal
    • -
    • Variable
    • -
    • Function
    • -
    • Function with type parameter
    • -
    • Index signature
    • -
    • Type alias
    • -
    • Type alias with type parameter
    • -
    -
      -
    • Enumeration
    • -
    • Enumeration member
    • -
    • Property
    • -
    • Method
    • -
    -
      -
    • Interface
    • -
    • Interface with type parameter
    • -
    • Constructor
    • -
    • Property
    • -
    • Method
    • -
    • Index signature
    • -
    -
      -
    • Class
    • -
    • Class with type parameter
    • -
    • Constructor
    • -
    • Property
    • -
    • Method
    • -
    • Accessor
    • -
    • Index signature
    • -
    -
      -
    • Inherited constructor
    • -
    • Inherited property
    • -
    • Inherited method
    • -
    • Inherited accessor
    • -
    -
      -
    • Protected property
    • -
    • Protected method
    • -
    • Protected accessor
    • -
    -
      -
    • Private property
    • -
    • Private method
    • -
    • Private accessor
    • -
    -
      -
    • Static property
    • -
    • Static method
    • -
    -
    -
    -
    -
    -

    Generated using TypeDoc

    -
    -
    - - - - \ No newline at end of file diff --git a/docs/interfaces/negativebrand.html b/docs/interfaces/negativebrand.html deleted file mode 100644 index 5af2219..0000000 --- a/docs/interfaces/negativebrand.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - NegativeBrand | guardian - - - - - -
    -
    -
    -
    - -
    -
    - Options -
    -
    - All -
      -
    • Public
    • -
    • Public/Protected
    • -
    • All
    • -
    -
    - - - - - - -
    -
    - Menu -
    -
    -
    -
    -
    -
    - -

    Interface NegativeBrand

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Brand interface for Negative

    -
    -
    -
    -
    -

    Hierarchy

    -
      -
    • - NegativeBrand -
    • -
    -
    -
    -

    Index

    -
    -
    -
    -

    Properties

    - -
    -
    -
    -
    -
    -

    Properties

    -
    - -

    __Negative__

    -
    __Negative__: void
    - -
    -
    -
    - -
    -
    -
    -
    -

    Legend

    -
    -
      -
    • Module
    • -
    • Object literal
    • -
    • Variable
    • -
    • Function
    • -
    • Function with type parameter
    • -
    • Index signature
    • -
    • Type alias
    • -
    • Type alias with type parameter
    • -
    -
      -
    • Enumeration
    • -
    • Enumeration member
    • -
    • Property
    • -
    • Method
    • -
    -
      -
    • Interface
    • -
    • Interface with type parameter
    • -
    • Constructor
    • -
    • Property
    • -
    • Method
    • -
    • Index signature
    • -
    -
      -
    • Class
    • -
    • Class with type parameter
    • -
    • Constructor
    • -
    • Property
    • -
    • Method
    • -
    • Accessor
    • -
    • Index signature
    • -
    -
      -
    • Inherited constructor
    • -
    • Inherited property
    • -
    • Inherited method
    • -
    • Inherited accessor
    • -
    -
      -
    • Protected property
    • -
    • Protected method
    • -
    • Protected accessor
    • -
    -
      -
    • Private property
    • -
    • Private method
    • -
    • Private accessor
    • -
    -
      -
    • Static property
    • -
    • Static method
    • -
    -
    -
    -
    -
    -

    Generated using TypeDoc

    -
    -
    - - - - \ No newline at end of file diff --git a/docs/interfaces/positivebrand.html b/docs/interfaces/positivebrand.html deleted file mode 100644 index baa90c9..0000000 --- a/docs/interfaces/positivebrand.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - PositiveBrand | guardian - - - - - -
    -
    -
    -
    - -
    -
    - Options -
    -
    - All -
      -
    • Public
    • -
    • Public/Protected
    • -
    • All
    • -
    -
    - - - - - - -
    -
    - Menu -
    -
    -
    -
    -
    -
    - -

    Interface PositiveBrand

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Brand interface for Positive

    -
    -
    -
    -
    -

    Hierarchy

    -
      -
    • - PositiveBrand -
    • -
    -
    -
    -

    Index

    -
    -
    -
    -

    Properties

    - -
    -
    -
    -
    -
    -

    Properties

    -
    - -

    __Positive__

    -
    __Positive__: void
    - -
    -
    -
    - -
    -
    -
    -
    -

    Legend

    -
    -
      -
    • Module
    • -
    • Object literal
    • -
    • Variable
    • -
    • Function
    • -
    • Function with type parameter
    • -
    • Index signature
    • -
    • Type alias
    • -
    • Type alias with type parameter
    • -
    -
      -
    • Enumeration
    • -
    • Enumeration member
    • -
    • Property
    • -
    • Method
    • -
    -
      -
    • Interface
    • -
    • Interface with type parameter
    • -
    • Constructor
    • -
    • Property
    • -
    • Method
    • -
    • Index signature
    • -
    -
      -
    • Class
    • -
    • Class with type parameter
    • -
    • Constructor
    • -
    • Property
    • -
    • Method
    • -
    • Accessor
    • -
    • Index signature
    • -
    -
      -
    • Inherited constructor
    • -
    • Inherited property
    • -
    • Inherited method
    • -
    • Inherited accessor
    • -
    -
      -
    • Protected property
    • -
    • Protected method
    • -
    • Protected accessor
    • -
    -
      -
    • Private property
    • -
    • Private method
    • -
    • Private accessor
    • -
    -
      -
    • Static property
    • -
    • Static method
    • -
    -
    -
    -
    -
    -

    Generated using TypeDoc

    -
    -
    - - - - \ No newline at end of file diff --git a/guardref/index.ts b/guardref/index.ts new file mode 100644 index 0000000..707ade9 --- /dev/null +++ b/guardref/index.ts @@ -0,0 +1,90 @@ +import * as fs from 'fs'; +import { Application, DeclarationReflection, ReflectionKind } from 'typedoc'; +import { PredicateType, ReferenceType } from 'typedoc/dist/lib/models'; +import options from '../typedoc.js'; + +/* eslint-disable no-sync */ +/* eslint-disable max-len */ + +// Create a new TypeDoc app with our project options +const app = new Application(options); + +// Convert the project +const project = app.convert([ 'src/index' ])!; + +interface FactoryParam { + name: string; + type: string; + typeRef: boolean; + description?: string; +} +interface GuardRef { + name: string; + factoryParams?: FactoryParam[]; + returnType: string; + returnTypeRef: boolean; + description?: string; +} + +const guardRefs: GuardRef[] = []; + +function isInternalRef(ref: string): boolean { + const reflection = project.findReflectionByName(ref); + if (!reflection) return false; + return reflection.kind !== ReflectionKind.TypeParameter; +} + +// Loop through each function relection, finding any guards. +for (const reflection of project.getReflectionsByKind(ReflectionKind.Function)) { + const declaration = reflection as DeclarationReflection; + const signature = declaration?.signatures?.[0]; + + // Skip declarations that do not have function signatures and are not in the guards directory + if (!signature || !signature.sources || !signature.sources[0].fileName.includes('src/guards/')) continue; + + // If it the return type is predicate type, this is a guard + if (signature.type instanceof PredicateType) { + let returnType = signature.type.targetType!.toString()!; + if (returnType === 'Function') returnType = returnType.toLowerCase(); + + guardRefs.push({ + name: declaration.name, + description: (signature.comment?.shortText ?? '').replace(/\n/ug, ''), + returnType, + returnTypeRef: isInternalRef(returnType), + }); + } + + // If the return type is a guard, this is a factory + if (signature.type instanceof ReferenceType && signature.type.name === 'Guard') { + const returnType = signature.type.typeArguments![0].toString(); + + guardRefs.push({ + name: declaration.name, + description: (signature.comment?.shortText ?? '').replace(/\n/ug, ''), + returnType, + returnTypeRef: isInternalRef(returnType), + factoryParams: (signature.parameters ?? []).map(param => ({ + name: param.name, + type: param.type!.toString(), + typeRef: isInternalRef(param.type!.toString()), + description: (param.comment?.text ?? '').replace(/\n/ug, ''), + })), + }); + } +} + +// Sort the guards alphabetically by name +guardRefs.sort((a, b) => a.name > b.name ? 1 : -1); + +// Build the markdown table +let table = guardRefs + .map(ref => `| [[${ref.name}]] | ${(ref.factoryParams ?? []).map(param => `__${param.name}__: ${param.typeRef ? `[[${param.type}]]` : param.type} - ${param.description}`).join('
    ')} | ${ref.returnTypeRef ? `[[${ref.returnType}]]` : ref.returnType} | ${ref.description}`) + .join('\n'); + +table = `\n| Name | Factory Params | Return Type | Description |\n| --- | --- | --- | --- |\n${table}\n`; + +// Read in the readme, replace the placeholder text, and rewrite it. +let readme = fs.readFileSync('README.md', 'utf8'); +readme = readme.replace(/(?:[\s\S]*)?/u, table); +fs.writeFileSync('README.md', readme); diff --git a/guardref/tsconfig.json b/guardref/tsconfig.json new file mode 100644 index 0000000..bd79bd4 --- /dev/null +++ b/guardref/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "lib": ["esnext", "dom"], + "types": ["node"], + "allowJs": true + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 28c89ba..f499be1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -480,6 +480,12 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, + "@types/node": { + "version": "12.12.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.21.tgz", + "integrity": "sha512-8sRGhbpU+ck1n0PGAUgVrWrWdjSW2aqNeyC15W88GRsMpSwzv6RJGlLhE7s2RhVSOdyDmxbqlWSeThq4/7xqlA==", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -615,6 +621,15 @@ "type-fest": "^0.8.1" } }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", @@ -630,6 +645,12 @@ "color-convert": "^1.9.0" } }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true + }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -640,6 +661,12 @@ "normalize-path": "^2.1.1" } }, + "arg": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.2.tgz", + "integrity": "sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -733,6 +760,15 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "autolinker": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz", + "integrity": "sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=", + "dev": true, + "requires": { + "gulp-header": "^1.7.1" + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1109,6 +1145,12 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, + "coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==", + "dev": true + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -1174,6 +1216,27 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + } + }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -1384,6 +1447,18 @@ "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", "dev": true }, + "diacritics-map": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", + "integrity": "sha1-bfwP+dAQAKLt8oZTccrDFulJd68=", + "dev": true + }, + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + }, "diff-sequences": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", @@ -1788,6 +1863,57 @@ } } }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "expect": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", @@ -2701,12 +2827,47 @@ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, + "gray-matter": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz", + "integrity": "sha1-MELZrewqHe1qdwep7SOA+KF6Qw4=", + "dev": true, + "requires": { + "ansi-red": "^0.1.1", + "coffee-script": "^1.12.4", + "extend-shallow": "^2.0.1", + "js-yaml": "^3.8.1", + "toml": "^2.3.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "dev": true }, + "gulp-header": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz", + "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==", + "dev": true, + "requires": { + "concat-with-sourcemaps": "*", + "lodash.template": "^4.4.0", + "through2": "^2.0.0" + } + }, "handlebars": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", @@ -3866,6 +4027,15 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "lazy-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", + "dev": true, + "requires": { + "set-getter": "^0.1.0" + } + }, "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", @@ -3888,6 +4058,47 @@ "type-check": "~0.3.2" } }, + "list-item": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz", + "integrity": "sha1-DGXQDih8tmPMs8s4Sad+iewmilY=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "extend-shallow": "^2.0.1", + "is-number": "^2.1.0", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -3916,6 +4127,12 @@ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -3928,6 +4145,25 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, "lodash.unescape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", @@ -4003,12 +4239,52 @@ "object-visit": "^1.0.0" } }, + "markdown-link": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz", + "integrity": "sha1-MsXGUZmmRXMWMi0eQinRNAfIx88=", + "dev": true + }, + "markdown-toc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz", + "integrity": "sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==", + "dev": true, + "requires": { + "concat-stream": "^1.5.2", + "diacritics-map": "^0.1.0", + "gray-matter": "^2.1.0", + "lazy-cache": "^2.0.2", + "list-item": "^1.1.1", + "markdown-link": "^0.1.1", + "minimist": "^1.2.0", + "mixin-deep": "^1.1.3", + "object.pick": "^1.2.0", + "remarkable": "^1.7.1", + "repeat-string": "^1.6.1", + "strip-color": "^0.1.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "marked": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", "dev": true }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4583,6 +4859,12 @@ "react-is": "^16.8.4" } }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -4627,6 +4909,25 @@ "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==", "dev": true }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, "react-is": { "version": "16.12.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", @@ -4654,6 +4955,21 @@ "read-pkg": "^3.0.0" } }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "realpath-native": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", @@ -4723,6 +5039,16 @@ "integrity": "sha512-EzIHww9xV2Kpqx+corS/I7OBmf2rZ0pKKJPsw5Dc+l6Zq1TslDmtRIP9maVn3UH+72MIXmn8zzDgP07ihQogUA==", "dev": true }, + "remarkable": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz", + "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==", + "dev": true, + "requires": { + "argparse": "^1.0.10", + "autolinker": "~0.28.0" + } + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -4988,6 +5314,15 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "set-getter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", + "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "dev": true, + "requires": { + "to-object-path": "^0.3.0" + } + }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -5422,6 +5757,15 @@ "function-bind": "^1.1.1" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -5437,6 +5781,12 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "strip-color": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz", + "integrity": "sha1-EG9l09PmotlAHKwOsM6LinArT3s=", + "dev": true + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -5531,6 +5881,16 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -5594,6 +5954,12 @@ "repeat-string": "^1.6.1" } }, + "toml": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz", + "integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==", + "dev": true + }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -5654,6 +6020,19 @@ } } }, + "ts-node": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.4.tgz", + "integrity": "sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + } + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", @@ -5699,6 +6078,12 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, "typedoc": { "version": "0.15.3", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.15.3.tgz", @@ -5847,6 +6232,12 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, "util.promisify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", @@ -6067,6 +6458,12 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", @@ -6125,6 +6522,12 @@ "camelcase": "^5.0.0", "decamelize": "^1.2.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 32c731f..c9f5e40 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "devDependencies": { "@types/jest": "^24.0.23", + "@types/node": "^12.12.21", "@typescript-eslint/eslint-plugin": "^2.10.0", "@typescript-eslint/parser": "^2.10.0", "eslint": "^6.7.2", @@ -32,7 +33,9 @@ "eslint-plugin-sort-imports-es6-autofix": "^0.5.0", "http-server": "^0.12.0", "jest": "^24.9.0", + "markdown-toc": "^1.2.0", "ts-jest": "^24.2.0", + "ts-node": "^8.5.4", "typedoc": "^0.15.3", "typescript": "^3.7.3" }, @@ -42,7 +45,7 @@ "compile:cjs": "tsc -p tsconfig.cjs.json", "compile:esm": "tsc -p tsconfig.esm.json", "clean": "rm -rf ./dist", - "docs": "typedoc --tsconfig tsconfig.build.json --out docs src/index.ts", + "docs": "ts-node -P guardref/tsconfig.json guardref && markdown-toc -i README.md && typedoc --tsconfig tsconfig.build.json --out docs src/index.ts", "serve-docs": "http-server -c-1 -o docs/", "test": "jest --no-cache", "lint": "eslint --fix \"src/**/*.ts\"", diff --git a/src/guards/is-big-int.ts b/src/guards/is-big-int.ts index 223ab70..8b5fafb 100644 --- a/src/guards/is-big-int.ts +++ b/src/guards/is-big-int.ts @@ -5,7 +5,7 @@ import { Guard } from '../types'; * @param value The value to test * @returns The result of the test */ -export function isBigInt(value: unknown): value is string { +export function isBigInt(value: unknown): value is bigint { return typeof value === 'bigint'; } (isBigInt as Guard).expectation = 'be a BigInt'; diff --git a/src/guards/is-boolean.ts b/src/guards/is-boolean.ts index a29c9ed..d6ee351 100644 --- a/src/guards/is-boolean.ts +++ b/src/guards/is-boolean.ts @@ -5,7 +5,7 @@ import { Guard } from '../types'; * @param value The value to test * @returns The result of the test */ -export function isBoolean(value: unknown): value is string { +export function isBoolean(value: unknown): value is boolean { return typeof value === 'boolean'; } (isBoolean as Guard).expectation = 'be a boolean'; diff --git a/src/guards/is-comparable.ts b/src/guards/is-comparable.ts index dc8f0fa..cd96bf5 100644 --- a/src/guards/is-comparable.ts +++ b/src/guards/is-comparable.ts @@ -1,12 +1,20 @@ -import { Comparable } from '../types'; +import { Comparable, Guard } from '../types'; import { hasProperties } from './has-properties'; import { isFunction } from './is-function'; +/** + * @ignore + */ +const guard = hasProperties({ + compareTo: isFunction, +}, 'Comparable'); + /** * Guard that tests if the value implements the [[Comparable]] interface * @param value The value to test * @returns The result of the test */ -export const isComparable = hasProperties({ - compareTo: isFunction, -}, 'Comparable'); +export function isComparable(value: unknown): value is Comparable { + return guard(value); +} +(isComparable as Guard).expectation = guard.expectation; diff --git a/src/guards/is-date.ts b/src/guards/is-date.ts index 3976ad9..a2d89d8 100644 --- a/src/guards/is-date.ts +++ b/src/guards/is-date.ts @@ -1,10 +1,17 @@ import { Guard } from '../types'; import { isInstanceOf } from './is-instance-of'; +/** + * @ignore + */ +const guard = isInstanceOf(Date); + /** * Guard that tests if the value is an instance of Date * @param value The value to test * @returns The result of the test */ -export const isDate = isInstanceOf(Date); +export function isDate(value: unknown): value is Date { + return guard(value); +} (isDate as Guard).expectation = 'be a date'; diff --git a/src/guards/is-email.ts b/src/guards/is-email.ts index 65575e6..812d1e3 100644 --- a/src/guards/is-email.ts +++ b/src/guards/is-email.ts @@ -1,17 +1,10 @@ -import { Guard } from '../types'; +import { Guard, Tagged } from '../types'; import { isMatch } from './is-match'; -/** - * Brand interface for [[Email]] - */ -interface EmailBrand { - __Email__: void; -} - /** * A string that is an email address */ -export type Email = string & EmailBrand; +export type Email = Tagged; /** * Regex for validating an email address. Found here: diff --git a/src/guards/is-empty-string.ts b/src/guards/is-empty-string.ts index 3d42ae3..3479000 100644 --- a/src/guards/is-empty-string.ts +++ b/src/guards/is-empty-string.ts @@ -1,10 +1,17 @@ import { Guard } from '../types'; import { isIdenticalTo } from './is-identical-to'; +/** + * @ignore + */ +const guard = isIdenticalTo(''); + /** * Guard that tests if the value is an empty string * @param value The value to test * @returns The result of the test */ -export const isEmptyString = isIdenticalTo(''); +export function isEmptyString(value: unknown): value is '' { + return guard(value); +} (isEmptyString as Guard).expectation = 'be an empty string'; diff --git a/src/guards/is-equatable.ts b/src/guards/is-equatable.ts index ce2f69d..a348b4d 100644 --- a/src/guards/is-equatable.ts +++ b/src/guards/is-equatable.ts @@ -1,12 +1,20 @@ -import { Equatable } from '../types'; +import { Equatable, Guard } from '../types'; import { hasProperties } from './has-properties'; import { isFunction } from './is-function'; +/** + * @ignore + */ +const guard = hasProperties({ + equals: isFunction, +}, 'Equatable'); + /** * Guard that tests if the value implements the [[Equatable]] interface * @param value The value to test * @returns The result of the test */ -export const isEquatable = hasProperties({ - equals: isFunction, -}, 'Equatable'); +export function isEquatable(value: unknown): value is Equatable { + return guard(value); +} +(isEquatable as Guard).expectation = guard.expectation; diff --git a/src/guards/is-integer.ts b/src/guards/is-integer.ts index 9a0deb9..ebd4ecc 100644 --- a/src/guards/is-integer.ts +++ b/src/guards/is-integer.ts @@ -1,17 +1,10 @@ -import { Guard } from '../types'; +import { Guard, Tagged } from '../types'; import { isNumber } from './is-number'; -/** - * Brand interface for [[Integer]] - */ -interface IntegerBrand { - __Integer__: void; -} - /** * A number that is an integer */ -export type Integer = number & IntegerBrand; +export type Integer = Tagged; /** * Guard that tests if the value is an integer diff --git a/src/guards/is-negative-integer.ts b/src/guards/is-negative-integer.ts index 9013077..b4e981c 100644 --- a/src/guards/is-negative-integer.ts +++ b/src/guards/is-negative-integer.ts @@ -8,10 +8,17 @@ import { and } from '../operators/and'; */ export type NegativeInteger = Negative & Integer; +/** + * @ignore + */ +const guard = and(isNegative, isInteger); + /** * Guard that tests if the value is a negative integer * @param value The value to test * @returns The result of the test */ -export const isNegativeInteger: Guard = and(isNegative, isInteger); +export function isNegativeInteger(value: unknown): value is NegativeInteger { + return guard(value); +} (isNegativeInteger as Guard).expectation = 'be a negative integer'; diff --git a/src/guards/is-negative.ts b/src/guards/is-negative.ts index 36c570c..b669534 100644 --- a/src/guards/is-negative.ts +++ b/src/guards/is-negative.ts @@ -1,17 +1,10 @@ -import { Guard } from '../types'; +import { Guard, Tagged } from '../types'; import { isNumber } from './is-number'; -/** - * Brand interface for [[Negative]] - */ -interface NegativeBrand { - __Negative__: void; -} - /** * A number that is negative */ -export type Negative = number & NegativeBrand; +export type Negative = Tagged; /** * Guard that tests if the value is a negative number diff --git a/src/guards/is-nil.ts b/src/guards/is-nil.ts index f9f62bc..8f6824c 100644 --- a/src/guards/is-nil.ts +++ b/src/guards/is-nil.ts @@ -1,10 +1,19 @@ +import { Guard } from 'types'; import { isNull } from './is-null'; import { isUndefined } from './is-undefined'; import { or } from '../operators/or'; +/** + * @ignore + */ +const guard = or(isNull, isUndefined); + /** * Guard that tests if the value is null or undefined * @param value The value to test * @returns The result of the test */ -export const isNil = or(isNull, isUndefined); +export function isNil(value: unknown): value is unknown { + return guard(value); +} +(isNil as Guard).expectation = guard.expectation; diff --git a/src/guards/is-positive-integer.ts b/src/guards/is-positive-integer.ts index fd0b818..5f5867d 100644 --- a/src/guards/is-positive-integer.ts +++ b/src/guards/is-positive-integer.ts @@ -8,10 +8,14 @@ import { and } from '../operators/and'; */ export type PositiveInteger = Positive & Integer; +const guard = and(isPositive, isInteger); + /** * Guard that tests if the value is a positive integer * @param value The value to test * @returns The result of the test */ -export const isPositiveInteger: Guard = and(isPositive, isInteger); +export function isPositiveInteger(value: unknown): value is PositiveInteger { + return guard(value); +} (isPositiveInteger as Guard).expectation = 'be a positive integer'; diff --git a/src/guards/is-positive.ts b/src/guards/is-positive.ts index 95f6219..9df419f 100644 --- a/src/guards/is-positive.ts +++ b/src/guards/is-positive.ts @@ -1,17 +1,10 @@ -import { Guard } from '../types'; +import { Guard, Tagged } from '../types'; import { isNumber } from './is-number'; -/** - * Brand interface for [[Positive]] - */ -interface PositiveBrand { - __Positive__: void; -} - /** * A number that is positive */ -export type Positive = number & PositiveBrand; +export type Positive = Tagged; /** * Guard that tests if the value is a positive number diff --git a/src/guards/is-symbol.ts b/src/guards/is-symbol.ts index 4529370..7276a00 100644 --- a/src/guards/is-symbol.ts +++ b/src/guards/is-symbol.ts @@ -5,7 +5,7 @@ import { Guard } from '../types'; * @param value The value to test * @returns The result of the test */ -export function isSymbol(value: unknown): value is string { +export function isSymbol(value: unknown): value is symbol { return typeof value === 'symbol'; } (isSymbol as Guard).expectation = 'be a symbol'; diff --git a/src/types.ts b/src/types.ts index fa686e8..79b0ce2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -55,3 +55,15 @@ export type ComparisonResult = -1|0|1|undefined; * Union of types that TypeScript can narrow to literal types */ export type Narrowable = string | number | boolean | undefined | null | void | {}; + +/** + * A type tag. + */ +type Tag = { + [K in T]: void; +}; + +/** + * A tagged type. + */ +export type Tagged = TValue & Tag; diff --git a/tsconfig.build.json b/tsconfig.build.json index ef1ef06..6b8b8b0 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,5 +1,6 @@ { "extends": "./tsconfig.json", + "include": ["src"], "exclude": ["src/**/*.spec.ts"], "compilerOptions": { "types": [] diff --git a/tsconfig.json b/tsconfig.json index c5bf9da..231d433 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "module": "commonjs", "target": "es6", // Target ES6, which covers most browsers and Node.js "lib": ["esnext"], // Explicitly set the lib so we exclude DOM, which isn't fully supported in Node.js. We need esnext for bigint support "types": ["jest"], // Remove any globals. Just add Jest for testing @@ -10,6 +11,7 @@ "outDir": "dist", // Output to the dist directory "esModuleInterop": true, // Allow import of CommonJS modules "experimentalDecorators": true, // Enable decorators so we can test the Assert decorator + "baseUrl": "src", } } \ No newline at end of file diff --git a/typedoc.js b/typedoc.js index 4e733b1..6ae1338 100644 --- a/typedoc.js +++ b/typedoc.js @@ -1,7 +1,7 @@ module.exports = { - mode: "file", + name: 'Guardian Documentation', + mode: 'file', excludePrivate: true, excludeProtected: true, - excludeNodeExported: true, ignoreCompilerErrors: true, };