Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Commit

Permalink
feat: add conveniences to ts-types
Browse files Browse the repository at this point in the history
update typescript to 3.7.4 for additional language features
  • Loading branch information
rbergman committed Jan 13, 2020
1 parent 5da7a9c commit b1814d6
Show file tree
Hide file tree
Showing 11 changed files with 619 additions and 54 deletions.
2 changes: 1 addition & 1 deletion packages/dev-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dependencies": {
"tslint": "^5.18.0",
"tslint-microsoft-contrib": "^5.2.1",
"typescript": "~3.1.6"
"typescript": "~3.7.4"
},
"scripts": {
"clean-all": "shx rm -rf node_modules"
Expand Down
4 changes: 2 additions & 2 deletions packages/dev-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@
"chalk": "2.4.2",
"mocha": "^5.2.0",
"nyc": "^13.3.0",
"prettier": "^1.18.2",
"prettier": "^1.19.1",
"pretty-quick": "^1.11.1",
"shelljs": "~0.8.3",
"sinon": "^5.1.1",
"source-map-support": "~0.5.12",
"ts-node": "^7.0.1",
"tslint": "^5.18.0",
"tslint": "^5.20.1",
"typedoc": "~0.15.0",
"typedoc-plugin-external-module-name": "~1.1.3",
"xunit-file": "^1.0.0"
Expand Down
9 changes: 9 additions & 0 deletions packages/ts-types/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ export class NamedError extends Error {
}
}

/**
* Indicates an unexpected type was encountered during a type-narrowing operation.
*/
export class AssertionFailedError extends NamedError {
constructor(message: string) {
super('AssertionFailedError', message);
}
}

/**
* Indicates an unexpected type was encountered during a type-narrowing operation.
*/
Expand Down
45 changes: 32 additions & 13 deletions packages/ts-types/src/narrowing/as.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { AnyArray, AnyConstructor, AnyFunction, AnyJson, JsonArray, JsonMap, Optional } from '../types';
import { AnyConstructor, AnyFunction, AnyJson, Dictionary, JsonArray, JsonMap, Optional } from '../types';
import {
isArray,
isBoolean,
isDictionary,
isFunction,
isInstance,
isJsonArray,
Expand Down Expand Up @@ -78,35 +79,53 @@ export function asBoolean(value: unknown, defaultValue?: boolean): Optional<bool
*
* @param value The value to test.
*/
export function asObject(value: unknown): Optional<object>;
export function asObject<T = object>(value: unknown): Optional<T>;
/**
* Narrows an `unknown` value to an `object` if it is type-compatible, or returns the provided default otherwise.
*
* @param value The value to test.
* @param defaultValue The default to return if `value` was undefined or of the incorrect type.
*/
export function asObject(value: unknown, defaultValue: object): object;
export function asObject<T = object>(value: unknown, defaultValue: T): T;
// underlying function
export function asObject(value: unknown, defaultValue?: object): Optional<object> {
return isObject(value) ? value : defaultValue;
export function asObject<T = object>(value: unknown, defaultValue?: T): Optional<T> {
return isObject<T>(value) ? value : defaultValue;
}

/**
* Narrows an `unknown` value to a plain `object` if it is type-compatible, or returns `undefined` otherwise.
*
* @param value The value to test.
*/
export function asPlainObject(value: unknown): Optional<object>;
export function asPlainObject<T = object>(value: unknown): Optional<T>;
/**
* Narrows an `unknown` value to an `object` if it is type-compatible, or returns the provided default otherwise.
*
* @param value The value to test.
* @param defaultValue The default to return if `value` was undefined or of the incorrect type.
*/
export function asPlainObject(value: unknown, defaultValue: object): object;
export function asPlainObject<T = object>(value: unknown, defaultValue: T): T;
// underlying function
export function asPlainObject(value: unknown, defaultValue?: object): Optional<object> {
return isPlainObject(value) ? value : defaultValue;
export function asPlainObject<T = object>(value: unknown, defaultValue?: T): Optional<T> {
return isPlainObject<T>(value) ? value : defaultValue;
}

/**
* Narrows an `unknown` value to a `Dictionary<T>` if it is type-compatible, or returns `undefined` otherwise.
*
* @param value The value to test.
*/
export function asDictionary<T = object>(value: unknown): Optional<Dictionary<T>>;
/**
* Narrows an `unknown` value to an `object` if it is type-compatible, or returns the provided default otherwise.
*
* @param value The value to test.
* @param defaultValue The default to return if `value` was undefined or of the incorrect type.
*/
export function asDictionary<T = object>(value: unknown, defaultValue: Dictionary<T>): Dictionary<T>;
// underlying function
export function asDictionary<T = object>(value: unknown, defaultValue?: Dictionary<T>): Optional<Dictionary<T>> {
return isDictionary<T>(value) ? value : defaultValue;
}

/**
Expand Down Expand Up @@ -141,17 +160,17 @@ export function asInstance<C extends AnyConstructor>(
*
* @param value The value to test.
*/
export function asArray(value: unknown): Optional<AnyArray>;
export function asArray<T = unknown>(value: unknown): Optional<T[]>;
/**
* Narrows an `unknown` value to an `object` if it is type-compatible, or returns the provided default otherwise.
*
* @param value The value to test.
* @param defaultValue The default to return if `value` was undefined or of the incorrect type.
*/
export function asArray(value: unknown, defaultValue: AnyArray): AnyArray;
export function asArray<T = unknown>(value: unknown, defaultValue: T[]): T[];
// underlying function
export function asArray(value: unknown, defaultValue?: AnyArray): Optional<AnyArray> {
return isArray(value) ? value : defaultValue;
export function asArray<T = unknown>(value: unknown, defaultValue?: T[]): Optional<T[]> {
return isArray<T>(value) ? value : defaultValue;
}

/**
Expand Down
196 changes: 196 additions & 0 deletions packages/ts-types/src/narrowing/assert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
* Copyright (c) 2018, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { AssertionFailedError } from '../errors';
import { AnyArray, AnyConstructor, AnyFunction, AnyJson, JsonArray, JsonMap, Nullable, Optional } from '../types';
import {
asArray,
asBoolean,
asDictionary,
asFunction,
asInstance,
asJsonArray,
asJsonMap,
asNumber,
asObject,
asPlainObject,
asString
} from './as';
import { toAnyJson } from './to';

/**
* Asserts that a given `condition` is true, or raises an error otherwise.
*
* @param condition The condition to test.
* @param message The error message to use if the condition is false.
* @throws {@link AssertionFailedError} If the assertion failed.
*/
export function assert(condition: boolean, message?: string): asserts condition {
if (!condition) {
throw new AssertionFailedError(message || 'Assertion condition was false');
}
}

/**
* Narrows a type `Nullable<T>` to a `T` or raises an error.
*
* Use of the type parameter `T` to further narrow the type signature of the value being tested is
* strongly discouraged unless you are completely confident that the value is of the necessary shape to
* conform with `T`. This function does nothing at either compile time or runtime to prove the value is of
* shape `T`, so doing so amounts to nothing more than performing a type assertion, which is generally a
* bad practice unless you have performed some other due diligence in proving that the value must be of
* shape `T`. Use of the functions in the `has` co-library are useful for performing such full or partial
* proofs.
*
* @param value The value to test.
* @param message The error message to use if `value` is `undefined` or `null`.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertNonNull<T = unknown>(value: Nullable<T>, message?: string): asserts value is T {
assert(value != null, message || 'Value is not defined');
}

/**
* Narrows an `unknown` value to a `string` if it is type-compatible, or raises an error otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertString(value: unknown, message?: string): asserts value is string {
assertNonNull(asString(value), message || 'Value is not a string');
}

/**
* Narrows an `unknown` value to a `number` if it is type-compatible, or raises an error otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertNumber(value: unknown, message?: string): asserts value is number {
assertNonNull(asNumber(value), message || 'Value is not a number');
}

/**
* Narrows an `unknown` value to a `boolean` if it is type-compatible, or raises an error otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertBoolean(value: unknown, message?: string): asserts value is boolean {
assertNonNull(asBoolean(value), message || 'Value is not a boolean');
}

/**
* Narrows an `unknown` value to an `object` if it is type-compatible, or raises an error otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertObject(value: unknown, message?: string): asserts value is object {
assertNonNull(asObject(value), message || 'Value is not an object');
}

/**
* Narrows an `unknown` value to an `object` if it is type-compatible and tests positively with {@link isPlainObject},
* or raises an error otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertPlainObject(value: unknown, message?: string): asserts value is object {
assertNonNull(asPlainObject(value), message || 'Value is not an object');
}

/**
* Narrows an `unknown` value to a `Dictionary<T>` if it is type-compatible and tests positively with {@link isDictionary},
* or raises an error otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertDictionary(value: unknown, message?: string): asserts value is object {
assertNonNull(asDictionary(value), message || 'Value is not an object');
}

/**
* Narrows an `unknown` value to instance of constructor type `T` if it is type-compatible, or raises an error
* otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertInstance<C extends AnyConstructor>(
value: unknown,
ctor: C,
message?: string
): asserts value is InstanceType<C> {
assertNonNull(asInstance(value, ctor), message || `Value is not an instance of ${ctor.name}`);
}

/**
* Narrows an `unknown` value to an `Array` if it is type-compatible, or raises an error otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertArray(value: unknown, message?: string): asserts value is AnyArray {
assertNonNull(asArray(value), message || 'Value is not an array');
}

/**
* Narrows an `unknown` value to an `AnyFunction` if it is type-compatible, or raises an error otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertFunction(value: unknown, message?: string): asserts value is AnyFunction {
assertNonNull(asFunction(value), message || 'Value is not a function');
}

/**
* Narrows an `unknown` value to an `AnyJson` if it is type-compatible, or returns `undefined` otherwise.
*
* See also caveats noted in {@link isAnyJson}.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was not a JSON value type.
*/
export function assertAnyJson(value: unknown, message?: string): asserts value is AnyJson {
assertNonNull(toAnyJson(value), message || 'Value is not a JSON-compatible value type');
}

/**
* Narrows an `AnyJson` value to a `JsonMap` if it is type-compatible, or raises an error otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertJsonMap(value: Optional<AnyJson>, message?: string): asserts value is JsonMap {
assertNonNull(asJsonMap(value), message || 'Value is not a JsonMap');
}

/**
* Narrows an `AnyJson` value to a `JsonArray` if it is type-compatible, or raises an error otherwise.
*
* @param value The value to test.
* @param message The error message to use if `value` is not type-compatible.
* @throws {@link AssertionFailedError} If the value was undefined.
*/
export function assertJsonArray(value: Optional<AnyJson>, message?: string): asserts value is JsonArray {
assertNonNull(asJsonArray(value), message || 'Value is not a JsonArray');
}
Loading

0 comments on commit b1814d6

Please sign in to comment.