ts-runtime-typecheck
v2.6.0
Published
A collection of common types for TypeScript along with dynamic type cast methods.
Downloads
765
Readme
ts-runtime-typecheck
Simple functions for validating complex data.
JavaScript is a very flexible language. Meaning it's easy to get tripped up by a value with an unexpected type. Even in TypeScript you occasionally have to deal with values which cannot be safely typed at compile time. This library provides a comprehensive selection of functions to simplify type checking code with clear and concise function calls. Ensuring strict type safety throughout your program, no matter the input.
Installation
Releases are available on the npm repository and our GitHub releases page. ESM and CJS formats are both included, as well as TypeScript type definition files. Both formats work without TypeScript if you prefer plain JS.
npm install ts-runtime-typecheck
Contents
- Installation
- Type Casts
- Type Checks
- Type Coerce
- Type Asserts
- JSON Types
- Ensuring an optional value is defined
- Array/Object of Type Casts
- Validating interfaces
- Union types
- Class instances
- Guarded functions
- Reference
- Changelog
Type Casts
Type Casts take an unknown
value as an argument, and return a typed value as the result. These functions take the form as{TYPE}
, for example asNumber
. If the input value does not match the required type the function will throw. This does not perform any coercion on the value, passing a string
of a number to asNumber
will cause it to throw.
import { asNumber } from 'ts-runtime-typecheck';
function square (input: unknown): number {
const value: number = asNumber(input);
return value * value;
}
square(10)
// 100
square()
// Error: Unable to cast undefined to number
square('10')
// Error: Unable to cast string to number
Type Casts are meant to primarily validate questionable values that are expected to be in a well defined structure. Such as network responses, interfacing with untyped JavaScript or reading data back from a file. If you are looking to validate a type, without throwing an error then take a look at Type Checks.
Optional Type Casts
In the situation you want to check a value meets an Optional
type there exists an alternate function for each type cast. These take the form asOpt{TYPE}
. Unlike the standard functions when a Nullish
value is passed in they will emit undefined
instead of throwing. If the input is not Nullish
, then it behaves the same as the standard type casts. If the type condition is met then it emits the value, otherwise it will throw.
Type Checks
Type Checks take an unknown
value as an argument, and return a boolean
indicating if the given value matches the required type. These functions take the form is{TYPE}
. In the correct situation TypeScript is capable of refining the type of a value through the use of these functions and flow analysis, like the below example.
import { isNumber } from 'ts-runtime-typecheck';
export function printSq (value: unknown) {
if (isNumber(value)) {
// inside this block `value` is a `number`
console.log(`${value} * ${value} = ${value * value}`);
}
else {
// inside this block `value` is `unknown`
console.log('Invalid input', value);
}
}
In addition all relevant Type Checks have an alternate variant that take the form isOpt{TYPE}
. These variants return true if the value meets the given type or Nullish
.
import { isOptNumber } from 'ts-runtime-typecheck';
export function printSq (input: unknown) {
if (isOptNumber(input)) {
// inside this block `input` is `number | undefined | null`
const value = input ?? 1; // use nullish coalescing operator to ensure value is number
console.log(`${value} * ${value} = ${value * value}`);
}
else {
// inside this block `input` is `unknown`
console.log('Invalid input', value);
}
}
Type Coerce
Type Coercion functions take an unknown
value as an argument, and convert it into a specific type. These functions take the format make{TYPE}
. Unlike the other functions this only works for small subset of types: number, string and boolean. They make a best effort to convert the type, but if the input is not suitable then they will throw an error. For instance passing a non-numeric string to makeNumber
will cause it to throw, as will passing a string that is not "true" | "false"
to makeBoolean
. While these functions will take any input value, this is to allow the input of values that have not been validated. The only valid input types for all 3 functions are number | string | boolean
. The intention here is to allow useful conversion, but prevent accidentally passing complex types.
There is an argument that makeString
could support using the toString
method of an object
, but the default toString
method returns the useless [object Object]
string. It is possible to detect if an object has implemented it's own toString
method, but is it correct to use it in this situation? That depends on the intention of the programmer. In the absence of a clear answer the line has been drawn at only accepting primitives.
import { makeNumber } from 'ts-runtime-typecheck';
makeNumber('80') // 80
makeNumber(80) // 80
makeNumber(true) // 1
makeNumber(false) // 0
makeNumber('hello') // Error: Unable to cast string to Number
makeNumber({
toString () { return 'hello' }
}) // Error: Unable to cast object to Number
Type Asserts
Type Assert functions accept an unknown
value and throw if the value does not meet the type requirement, they do not return a value. While this may seem very similar to Type Casts they are capable of providing a hint to the TypeScript compiler without needing to reassign the value. As such they are very helpful for validating function arguments before using them.
Each type assert takes an optional second argument that is a label for the passed value, this will be included in the thrown TypeAssertion
error if the value does not meet the type requirement, making it easier to isolate the type violation.
import { assertDefined } from 'ts-runtime-typecheck';
function main (meaningOfLife: Optional<number>) {
meaningOfLife // number | null | undefined
assertDefined(meaningOfLife, 'Meaning of Life');
meaningOfLife // number
return 'but what is the question?';
}
main(42); // 'but what is the question?'
main(); // TypeAssertion: Meaning of Life is not defined
TypeAsserts cannot be based on generic types, due to limits in the TypeScript type system. Hence there is no analogous function to isStruct
and similar TypeCheck
. However, there is an alternative. It's possible to utilise an invariant ( or assert) function with a TypeCheck
to get the same effect, and an implementation of invariant
is provided for this purpose.
import { invariant, isLiteral } from 'ts-runtime-typecheck';
function main (meaningOfLife: unknown) {
meaningOfLife // unknown
invariant(isLiteral(42)(meaningOfLife), "Universe is broken, meaning of life isn't 42!");
meaningOfLife // 42
return 'but what is the question?';
}
main(42); // 'but what is the question?'
main(); // TypeAssertion: Universe is broken, meaning of life isn't 42!
JSON Types
Dealing with validating JSON values can often be frustrating, so to make it a little easier JSON specific types and checks are provided. Using the JSONValue
type in your code will ensure that TS statically analyses any literal values as serializable to JSON.
import type { JSONArray, JSONObject, JSONValue } from 'ts-runtime-typecheck';
// JSONArray is an Array of JSONValues
const a: JSONArray = [12, 'hello'];
// JSONObject is a Dictionary of JSONValues
const b: JSONObject = {
num: 12,
str: 'hello'
};
// JSONValue can be any of the following: JSONObject, JSONArray, string, number, boolean or null
const c: JSONValue = 12;
const d: JSONValue = new Error('hi'); // Type 'Error' is not assignable to type 'JSONValue'
For dynamic data isJSONValue
and asJSONValue
provide recursive type validation on a value.
Type Check and Type Casts are provided for JSONArrays
and JSONObjects
, with the caveat that they only accept JSONValues
. This is to avoid needing to recursively validate values which have already been validated.
import { asJSONValue, isJSONObject, isJSONArray } from 'ts-runtime-typecheck';
import type { JSONValue } from 'ts-runtime-typecheck';
function main (a: unknown) {
const obj: JSONValue = asJSONValue(a);
// obj: JSONValue
if (isJSONArray(obj)) {
// obj: JSONArray
}
else if (isJSONObject(obj)) {
// obj: JSONObject
}
else {
// obj: number | string | boolean | null
}
}
One other caveat of JSONValue
is that it does not guarantee that the value is not cyclic. It is not possible to serialize cyclic object with JSON.stringify
, but they are otherwise valid. Using isJSONValue
or asJSONValue
on a cyclic object will fail.
import { asJSONValue } from 'ts-runtime-typecheck';
import type { Dictionary } from 'ts-runtime-typecheck';
const almost_right: Dictionary = {};
almost_right.self = almost_right;
// BANG! this will fail, it recurses endlessly
const obj = asJSONValue(almost_right);
Ensuring an optional value is defined
A common situation is that you have an Optional
value, with a well defined type. At a specific time it should be defined, but the type system is not aware of this. TypeScript will allow you to cast the value to a non-optional type using !
, but this is often discouraged in style guides. A safer alternative is to use asDefined
and friends, which can be used to subtract the optionality from the type.
import { asDefined, assertDefined, isDefined } from 'ts-runtime-typecheck';
function doThing (value: number) {
// [...]
}
function branching (value?: number) {
// used for conditions, allows for an alternative to throwing or custom error behavior
if (isDefined(value)) {
doThing(value)
} else {
console.log('bad things')
}
}
function inline (value?: number) {
// used inline, throws if the value is Nullish otherwise returns the value
doThing(asDefined(value))
}
function assertion (value?: number) {
// used to ensure execution doesn't progress if the value isn't defined, throws if the value is Nullish
assertDefined(value)
doThing(value)
}
Array/Object of Type Casts
Validating that a value is an array or dictionary is easy enough, but how about the type of the contents? asArrayOf
and asDictionaryOf
allow you to cast the elements of a collection using a user defined Type Check. For example, to cast to Array<string>
:
import { isString, asArrayOf } from 'ts-runtime-typecheck';
function main (obj: unknown) {
const asStringArray = asArrayOf(isString);
const arr: string[] = asArrayOfString(obj);
}
Or Array<Dictionary<number>>
:
import { isNumber, isDictionaryOf, asArrayOf } from 'ts-runtime-typecheck';
function main () {
const isDictionaryOfNumber = isDictionaryOf(isNumber);
const asArrayOfDictionaryOfNumber = asArrayOf(isDictionaryOfNumber);
const arr = asArrayOfDictionaryOfNumber([
{
a: 12,
b: 42
},
{
n: 90
}
]);
}
Validating interfaces
Validating the shape of an object using a combination of asDictionary
and other Type Casts specific to property types can be a bit verbose. To simplify this scenario you can use asStruct
. This function takes an InterfacePattern
that defines a specific structure and returns a new function that will cast an unknown value to that structure. An InterfacePattern
is a fancy name for a Dictionary
of Type Checks.
import { asStruct, isString, isNumber } from 'ts-runtime-typecheck';
interface Item {
name: string;
value: number;
}
const asItem = asStruct({ name: isString, value: isNumber })
function main (obj: unknown) {
const item: Item = asItem(obj);
console.log(`${item.name} = ${item.value}`);
}
There is also a Type Check variant of the this function called isStruct
which works in a very similar way. As an InterfacePattern
is composed of Type Check functions it's possible to compose nested interface Type Checks.
import { asStruct, isStruct, isString, isOptString, isNumber } from 'ts-runtime-typecheck';
import type { Optional } from 'ts-runtime-typecheck';
interface Declaration {
item: Item;
description: Optional<string>
}
interface Item {
name: string;
value: number;
}
const isItem = isStruct({ name: isString, value: isNumber });
const asDeclaration = asStruct({ item: isItem, description: isOptString });
function main (obj: unknown) {
const { item, description } = asDeclaration(obj);
const comment: string = description ? `// ${description}` : '';
console.log(`${item.name} = ${item.value} ${comment}`);
}
Union types
When a value can be 2 or more types it is relatively easy to do Type Check.
import { isString, isArray } from 'ts-runtime-typecheck';
if (isString(a) || isArray(a)) {
// a: string | unknown[]
}
But you can't cast to that type, or pass it into a function like asArrayOf
or isStruct
which require a Type Check for their input. To do this you can use isUnion
or asUnion
. These functions take a variable number of Type Checks and produce a union of them.
import {
isString,
isArray,
isUnion,
asArrayOf
} from 'ts-runtime-typecheck';
const check = asArrayOf(isUnion(isString, isArray));
const b = check(['hello', [0, 1, 2], 'world']);
Class instances
Under most scenarios you will know if a value is an instance of a given class. However, there are scenarios where this is not the case. For these situations you can use isInstance
or asInstance
to ensure you have the correct type.
import { isInstance } from 'ts-runtime-typecheck';
function print_error (err) {
if (isInstance(Error)(err)) {
print_string(err.message);
} else {
print_unknown(err)
}
}
When validating a value matches an interface it may be desirable to instead use isInstance
instead of isStruct
. While it doesn't provide the same guarantees it will often be significantly faster, as it does not perform a Type Check on each member to see that they exist and contain the right type of value.
Literal types
TypeScript supports value types for Primitive
s. For example
let a: 'hello world' = 'hello world';
a = 'goodbye world'; // Type '"goodbye world"' is not assignable to type '"hello world"'
You can use asLiteral
and isLiteral
to construct TypeCast
s and TypeCheck
s respectively for these value types. These checks can be particularly useful for discriminated unions.
interface A {
type: 'a';
value: number;
}
interface B {
type: 'b';
value: number;
}
const isA = isStruct({
type: isLiteral('a'),
value: isNumber,
});
function main (n: A | B) {
n // A | B
if (isA(n)) {
n // A
} else {
n // B
}
}
Guarded Functions
TypeScript provides excellent compile time protections against misusing functions. The number of parameters, the types of parameters and the return type of the function are all statically type checked. But it provides no runtime protections. In a pure TS project this isn't normally required, but sometimes you need to pass around abstract functions and have them behave reliably. Alternatively you could be authoring a library and want to ensure that developers using your library without TS use your functions correctly. In both these scenarios you can wrap the function using asGuardedFunction
, specifying the return and parameter types. This will return a new function that when called will validate the incoming arguments, and the outgoing return value at runtime. If the types don't match up it will throw an error. The resulting function will have accurate parameter and return types, irrespective of the original function.
import { asGuardedFunction } from 'ts-runtime-typecheck';
export const publicFunction = asGuardedFunction(
(a: number, b: number) => `${(a / b) * 100}%`, // function to wrap
isString, // return value
isNumber, isNumber // parameters
);
Reference
Reference: Type Casts
asString
Cast
unknown
tostring
.asNumber
Cast
unknown
tonumber
.asIndex
Cast
unknown
toIndex
.asPrimitive
Cast
unknown
toPrimitive
.asIndexable
Cast
unknown
toIndexable
.asBoolean
Cast
unknown
toboolean
.asArray
Cast
unknown
toArray<unknown>
.asDictionary
Cast
unknown
toDictionary<unknown>
.asFunction
Cast
unknown
toUnknownFunction
.asDefined
Cast
Type | Nullish
toType
, whereType
is a generic parameter.asJSONValue
Cast
unknown
toJSONValue
. This function recursively validates the value, and hence will fail if given a cyclic value.asJSONObject
Cast
JSONValue
toJSONObject
. UnlikeasJSONValue
this does not perform recursive validation, hence it only accepts aJSONValue
so that the sub-elements are of a known type.asJSONArray
Cast
JSONValue
toJSONArray
. UnlikeasJSONValue
this does not perform recursive validation, hence it only accepts aJSONValue
so that the sub-elements are of a known type.asArrayOf
Takes a Type Cast function for
Type
and returns a new Type Cast function forArray<Type>
where type is a generic parameter. Refer to Array/Object of Type Casts for examples.asDictionaryOf
Takes a Type Cast function for
Type
and returns a new Type Cast function forDictionary<Type>
where type is a generic parameter. Refer to Array/Object of Type Casts for examples.asStruct
Takes an
InterfacePattern
which is equivalent toType
and returns a new Type Cast function forType
, whereType
is an interface defined by theTypeChecks
specified in the pattern. Refer to Validating interfaces for examples.asInstance
Takes a class (not a instance of a class) and returns a new Type Cast for an instance of that class.
asLiteral
Takes a Primitive value and returns a new Type Cast for that specific value.
asGuardedFunction
Takes a unknown value and a series of
TypeChecks
and returns a strongly typed function. It will be a new function that wraps the original with runtime type validation, ensuring that the arguments and return value are the expected types. The firstTypeCheck
is for the return value, and subsequent variadicTypeChecks
are used for validating arguments.
Reference: Optional Type Casts
asOptString
Cast
unknown
value tostring | undefined
. If value isNullish
then returnundefined
.asOptNumber
Cast
unknown
value tonumber | undefined
. If value isNullish
then returnundefined
.asOptIndex
Cast
unknown
value toIndex | undefined
. If value isNullish
then returnundefined
.asOptPrimitive
Cast
unknown
value toPrimitive | undefined
. If value isNullish
then returnundefined
.asOptIndexable
Cast
unknown
value toIndexable | undefined
. If value isNullish
then returnundefined
.asOptBoolean
Cast
unknown
value toboolean | undefined
. If value isNullish
then returnundefined
.asOptArray
Cast
unknown
value toArray<unknown> | undefined
. If value isNullish
then returnundefined
.asOptDictionary
Cast
unknown
value toDictionary<unknown> | undefined
. If value isNullish
then returnundefined
.asOptFunction
Cast
unknown
value toUnknownFunction | undefined
. If value isNullish
then returnundefined
.asUnion
Takes a variable number of type checks as parameters
<A>(...checks: TypeCheck<A>[])
and returns a new type cast that composes them into type cast for the unionA
. This allows creating a cast for a type union by composing any existing type checks.asOptUnion
Identical to
asUnion
but it the resulting cast returnsA | null | undefined
.asOptJSONValue
Cast
unknown
value toJSONValue | undefined
. If value isNullish
then returnundefined
.asOptJSONObject
Cast
JSONValue | undefined
value toJSONObject | undefined
. If value isNullish
then returnundefined
.asOptJSONArray
Cast
JSONValue | undefined
value toJSONArray | undefined
. If value isNullish
then returnundefined
.asOptArrayOf
Takes a Type Cast function for
Type
and returns a new Type Cast function forArray<Type> | undefined
where type is a generic parameter. Refer to Array/Object of Type Casts for examples.asOptDictionaryOf
Takes a Type Cast function for
Type
and returns a new Type Cast function forDictionary<Type> | undefined
where type is a generic parameter. Refer to Array/Object of Type Casts for examples.asOptStruct
Takes an
InterfacePattern
which is equivalent toType
and returns a new Type Cast function forType | undefined
, whereType
is an interface defined by theTypeAsserts
specified in the pattern. Refer to Validating interfaces for examples.asOptInstance
Takes a class (not a instance of a class) and returns a new Type Cast for a Optional instance of that class.
asOptLiteral
Takes a Primitive value and returns a new optional Type Cast for that specific value.
Reference: Type Checks
isDictionary
Takes an
unknown
value and returns a boolean indicating if the value is of the typeDictionary<unknown>
.isFunction
Takes an
unknown
value and returns a boolean indicating if the value is of the typeUnknownFunction
.isBoolean
Takes an
unknown
value and returns a boolean indicating if the value is of the typeboolean
.isString
Takes an
unknown
value and returns a boolean indicating if the value is of the typestring
.isNumber
Takes an
unknown
value and returns a boolean indicating if the value is of the typenumber
.isIndex
Takes an
unknown
value and returns a boolean indicating if the value is of the typeIndex
.isPrimitive
Takes an
unknown
value and returns a boolean indicating if the value is of the typePrimitive
.isIndexable
Takes an
unknown
value and returns a boolean indicating if the value is of the typeIndexable
.isArray
Takes an
unknown
value and returns a boolean indicating if the value is of the typeArray<unknown>
.isUndefined
Takes an
unknown
value and returns a boolean indicating if the value is of the typeundefined
.isNullish
Takes an
unknown
value and returns a boolean indicating if the value is of the typeNullish
.isDefined
Takes an
unknown
value and returns a boolean indicating if the value is not of the typeNullish
.isUnion
Takes a variable number of type checks as parameters
<A>(...checks: TypeCheck<A>[])
and returns a new type check that composes them into union type checkTypeCheck<A>
. This allows creating a test for a type union by composing any existing type checks. For inline code it will generally make sense to use logical OR operators instead of this, for exampleif ( isNumber(n) || isArray(n) ) {}
. This particular function is intended for when you wish to compose a type check or cast that contains a union, or create a library type check for a common union type.isJSONValue
Takes an
unknown
value and returns a boolean indicating if the value is of the typeJSONValue
.isJSONArray
Takes an
JSONValue
value and returns a boolean indicating if the value is of the typeJSONArray
.isJSONObject
Takes an
JSONValue
value and returns a boolean indicating if the value is of the typeJSONObject
.isArrayOf
Takes a Type Check function for
Type
and returns a new Type Check function forArray<Type>
where Type is a generic parameter.isDictionaryOf
Takes a Type Check function for
Type
and returns a new Type Check function forDictionary<Type>
where Type is a generic parameter.isStruct
Takes an
InterfacePattern
which is equivalent toType
and returns a newTypeAssert
function forType
, whereType
is an interface defined by theTypeAsserts
specified in the pattern. Refer to Validating interfaces for examples.isInstance
Takes a class (not a instance of a class) and returns a new Type Check for an instance of that class.
isLiteral
Takes a Primitive value and returns a new Type Check for that specific value.
Reference: Optional Type Checks
isOptDictionary
Takes an
unknown
value and returns a boolean indicating if the value is of the typeOptional<Dictionary<unknown>>
.isOptFunction
Takes an
unknown
value and returns a boolean indicating if the value is of the typeOptional<UnknownFunction>
.isOptBoolean
Takes an
unknown
value and returns a boolean indicating if the value is of the typeOptional<boolean>
.isOptString
Takes an
unknown
value and returns a boolean indicating if the value is of the typeOptional<string>
.isOptNumber
Takes an
unknown
value and returns a boolean indicating if the value is of the typeOptional<number>
.isOptPrimitive
Takes an
unknown
value and returns a boolean indicating if the value is of the typeOptional<Primitive>
.isOptIndex
Takes an
unknown
value and returns a boolean indicating if the value is of the typeOptional<Index>
.isOptIndexable
Takes an
unknown
value and returns a boolean indicating if the value is of the typeOptional<Indexable>
.isOptArray
Takes an
unknown
value and returns a boolean indicating if the value is of the typeOptional<Array<unknown>>
.isOptUnion
Identical to
isUnion
but it the resulting typecheck isTypeCheck<A | null | undefined>
.isOptJSONValue
Takes an
unknown
value and returns a boolean indicating if the value is of the typeOptional<JSONValue>
.isOptJSONArray
Takes an
Optional<JSONValue>
value and returns a boolean indicating if the value is of the typeOptional<JSONArray>
.isOptJSONObject
Takes an
Optional<JSONValue>
value and returns a boolean indicating if the value is of the typeOptional<JSONObject>
.isOptStruct
Takes an
InterfacePattern
which is equivalent toType
and returns a newTypeAssert
function forOptional<Type>
, whereType
is an interface defined by theTypeAsserts
specified in the pattern. Refer to Validating interfaces for examples.isOptArrayOf
Takes a Type Check function for
Type
and returns a new Type Check function forOptional<Array<Type>>
where Type is a generic parameter.isOptDictionaryOf
Takes a Type Check function for
Type
and returns a new Type Check function forOptional<Dictionary<Type>>
where Type is a generic parameter.isOptInstance
Takes a class (not a instance of a class) and returns a new Type Check for a Optional instance of that class.
isOptLiteral
Takes a Primitive value and returns a new optional Type Check for that specific value.
Reference: Type Coerce
makeString
Takes an
unknown
value and converts it to it's textual representation. A value that cannot be cleanly converted will trigger an error.makeNumber
Takes an
unknown
value and converts it to it's numerical representation. A value that cannot be cleanly converted will trigger an error.makeBoolean
Takes an
unknown
value and converts it to it's boolean representation. A value that cannot be cleanly converted will trigger an error.makeStrictPartial
Takes a value of the generic type
T
and returns a copy of the object excluding any members that wereNullish
. The returned object meets the typeStrictPartial<T>
.
Reference: Type Assert
assertDefined
Assert value of type Type | Nullish
is Type
, where Type
is a generic parameter. Accepts an optional name for the value that is included in the error if the value is nullish.
assertNumber
Assert value of type unknown
is number
. Accepts an optional name for the value that is included in the error if the value is not a number.
assertString
Assert value of type unknown
is string
. Accepts an optional name for the value that is included in the error if the value is not a string
.
assertBoolean
Assert value of type unknown
is boolean
. Accepts an optional name for the value that is included in the error if the value is not a boolean
.
assertIndex
Assert value of type unknown
is Index
. Accepts an optional name for the value that is included in the error if the value is not a Index
.
assertPrimitive
Assert value of type unknown
is Primitive
. Accepts an optional name for the value that is included in the error if the value is not a Primitive
.
assertArray
Assert value of type unknown
is unknown[]
. Accepts an optional name for the value that is included in the error if the value is not a unknown[]
.
assertDictionary
Assert value of type unknown
is Dictionary
. Accepts an optional name for the value that is included in the error if the value is not a Dictionary
.
assertIndexable
Assert value of type unknown
is Indexable
. Accepts an optional name for the value that is included in the error if the value is not a Indexable
.
assertFunction
Assert value of type unknown
is UnknownFunction
. Accepts an optional name for the value that is included in the error if the value is not a UnknownFunction
.
assertOptNumber
Assert value of type unknown
is Optional<number>
. Accepts an optional name for the value that is included in the error if the value is not a Optional<number>
.
assertOptString
Assert value of type unknown
is Optional<string>
. Accepts an optional name for the value that is included in the error if the value is not a Optional<string>
.
assertOptBoolean
Assert value of type unknown
is Optional<boolean>
. Accepts an optional name for the value that is included in the error if the value is not a Optional<boolean>
.
assertOptIndex
Assert value of type unknown
is Optional<Index>
. Accepts an optional name for the value that is included in the error if the value is not a Optional<Index>
.
assertOptPrimitive
Assert value of type unknown
is Optional<Primitive>
. Accepts an optional name for the value that is included in the error if the value is not a Optional<Primitive>
.
assertOptArray
Assert value of type unknown
is Optional<unknown[]>
. Accepts an optional name for the value that is included in the error if the value is not a Optional<unknown[]>
.
assertOptDictionary
Assert value of type unknown
is Optional<Dictionary>
. Accepts an optional name for the value that is included in the error if the value is not a Optional<Dictionary>
.
assertOptIndexable
Assert value of type unknown
is Optional<Indexable>
. Accepts an optional name for the value that is included in the error if the value is not a Optional<Indexable>
.
assertOptFunction
Assert value of type unknown
is Optional<UnknownFunction>
. Accepts an optional name for the value that is included in the error if the value is not a Optional<UnknownFunction>
.
assertJSONValue
Assert value of type unknown
is JSONValue
. Accepts an optional name for the value that is included in the error if the value is not a JSONValue
.
assertJSONArray
Assert value of type JSONValue
is JSONArray
. Accepts an optional name for the value that is included in the error if the value is not a JSONArray
.
assertJSONObject
Assert value of type JSONValue
is JSONObject
. Accepts an optional name for the value that is included in the error if the value is not a JSONObject
.
assertOptJSONValue
Assert value of type unknown
is JSONValue | undefined
. Accepts an optional name for the value that is included in the error if the value is not a JSONValue | undefined
.
assertOptJSONArray
Assert value of type JSONValue | undefined
is JSONArray | undefined
. Accepts an optional name for the value that is included in the error if the value is not a JSONArray | undefined
.
assertOptJSONObject
Assert value of type JSONValue | undefined
is JSONObject | undefined
. Accepts an optional name for the value that is included in the error if the value is not a JSONObject | undefined
.
Reference: Helper functions
invariant
An invariant is a logical declaration about a condition which the programmer knows to be true, but the compiler cannot. Many of the patterns in ts-runtime-typecheck are based around this concept, albeit to encourage a stricter and safer environment. This helper accepts a logical condition and a message. If the condition is true nothing happens, if it's false then a
TypeAssertion
is thrown with the given message. If the condition is the result of aTypeCheck
then the type predicate is enforced by the invariant.import { invariant, isString } from 'ts-runtime-typecheck'; function main (username: unknown) { invariant(isString(username), 'Expected username to be a string'); username // string }
inspectType
Inspects the type of a given value and returns a description of it as a string. Useful for constructing debug messages from unknown values. Instances of classes are described using the name of their class. Abstract objects are traversed recursively so that their elements can be described as well. Recursion uses a depth limit to prevent overlarge descriptions. Once the limit has been reached abstract objects will be described as Dictionary
.
An optional second argument can be passed to modify the default behaviour. For example you can change the maxDepth
limit from it's default of 3
; a value of 0
would prevent recursion whereas Infinity
would remove the limit.
Arrays currently do not feature recursive type description, but this might be introduced in future.
Internally this is used to describe values in type error messages.
import { inspectType } from 'ts-runtime-typecheck';
class Example {}
inspectType('hello world'); // string
inspectType(null) // null
inspectType([]) // Array
inspectType(new Example) // Example
inspectType({}) // {}
inspectType({
foo: 'hello',
bar: 'world'
}) // { foo: string, bar: string }
inspectType({ foo: 'bar' }, { maxDepth: 0 }); // Dictionary
Reference: Types
JSONValue
A union of all the JSON compatible types:
JSONArray
,JSONObject
,number
,string
,boolean
,null
.JSONObject
An alias to
Dictionary<JSONValue>
which can represent any JSONObject
value.JSONArray
An alias to
Array<JSONValue>
which can represent any JSONArray
value.Dictionary
An alias to
Record<string, Type>
whereType
is a generic parameter that default tounknown
. This type can be used to represent a typical key-value map constructed from a JSObject
. Where possible useMap
instead, as it is specifically designed for this purpose and has better protection against null errors in TS.Index
A union of the
number
andstring
types that represent a value that could be used to index an element within a JSObject
.Primitive
A union of the
number
,string
andboolean
types that are the key primitive values in JS.Indexable
An alias to
Record<Index, Type>
whereType
is a generic parameter that default tounknown
. This type can be used to represent an unknown key-value object that can be indexed using anumber
orstring
. It is intended to be used to ease the transition of JS project to TS. Where possible useDictionary
or preferablyMap
instead, as it is specifically designed for this purpose and has better protection against null errors in TS.Nullish
A union of
undefined
andnull
. Generally preferable to eithernull
orundefined
on non-validated input. However, be aware of varying behavior between these 2 types in JS around optional members, default parameters and equality.Optional
A union of
Type
andNullish
whereType
is a generic parameter.UnknownFunction
A stricter alternative to the type
Function
. It accepts any number of unknown parameters, and returns an unknown value. Allowing you to reference an untyped function in a slightly safer manner. This does not provide any arity or type checks for the parameters.UnknownAsyncFunction
Identical to
UnknownFunction
in all ways but 1, it returnsPromise<unknown>
instead.TypeCheck
An alias for a function that meets the requirements of TypeScript Type Guards. They take the format
(value: unknown) => value is TYPE
. With the exception of specialist JSON checks all TypeChecks conform to this type.InterfacePattern
An alias for a
Dictionary
ofTypeAssert
functions. When used in conjunction withisStruct
orasStruct
they can validate anobject
against the equivalent interface to the pattern.StrictRequired
A variant of the inbuilt
Required<T>
, which is the opposite ofOptional<T>
in that it subtracts the typeundefined
from each member of the typeT
. StrictRequired varies in that it also subtracts the typenull
from each member. Ensuring that all members meet the constraintNonNullable
.StrictPartial
A variant of the
Partial<T>
inbuilt, and closely related toFuzzyPartial
.Partial
makes no guarantees about the members of the typeT
, as such they can be unions ofnull
. This can introduce inconsistency for users of the type; expecting that members can be specified using eithernull
orundefined
, where only some can also usenull
.StrictPartial
resolves this by specifying that no members of the typeT
can benull
, ensuring a consistent interface.FuzzyPartial
A variant of the
Partial<T>
inbuilt, and closely related toStrictPartial
.Partial
makes no guarantees about the members of the typeT
, as such they can be unions ofnull
. This can introduce inconsistency for users of the type; expecting that members can be specified using eithernull
orundefined
, where only some can also usenull
.FuzzyPartial
resolves this by specifying that all members of the typeT
can ALSO benull
, ensuring a consistent interface.TypeAssertion
A custom error with the name
TypeAssertion
. This type is exported as a value so that Errors of this type can be isolated from other errors using instance checks. It is possible to use the constructor to create and throw your own Errors if you wish, but this may change in future.
Changelog
1.0.0
- Initial release
1.1.0
- Documentation update.
- Fix: asDefined was returning
unknown
. - Breaking change: rename ObjectDict to Dictionary.
- Add: Nullish type (
null | undefined
). - Change: Dictionary no longer contains
T | undefined
union. - Change: Optional type now also includes
null
in the type union.
1.1.1
- Change: return type of
asOpt{TYPE}
is nowTYPE | undefined
instead ofOptional<TYPE>
( removes null from union ) - Documentation corrections.
1.2.0
- Add: Introduce
isStruct
andasStruct
that allow the inspection of a object to see if it meets a specific interface. - Add: Optional variants of Type Checks with form
isOpt{TYPE}
. - Breaking Change:
asDefined
can longer acceptnull
as a fallback parameter. - Change:
asIndexable
now accepts arrays. - Add:
isIndexable
type check. - Change: Expose the
TypeAssert
type publicly. - Add:
InterfacePattern
type. - Change: modify the type names in errors to be closer to the TypeScript names.
2.0.0
- Add:
isUnion
andisOptUnion
to allow checking if a value matches any type of a type union. - Add:
asUnion
andasOptUnion
to allow casting a value to a type union. - Add:
isArrayOf
andisOptArrayOf
to allow checking if a value is an array of a given type. - Add:
isDictionaryOf
andisOptDictionaryOf
to allow checking if a value is a Dictionary of a given type. - Breaking Change: Recursive Type Casts now take a Type Check as an argument instead of a Type Cast, and no longer emit a copy of the input. As a side effect if you are upgrading from
asArrayRecursive(asOptString)
toasArraryOf(isOptString)
or (similar) the output array may containnull
as the elements are no longer transformed by an inner cast ( optional cast methods normalize output toundefined
). - Add:
isInstance
andisOptInstance
to allow checking if a value is an instance of a given class. - Add:
asInstance
andasOptInstance
to allow casting a value to an instance of a given class. - Breaking Change:
asRecord
,asOptRecord
,asRecordRecursive
andasOptRecordRecursive
have been renamed toasDictionary
,asOptDictionary
,asDictionaryOf
andasOptDictionaryOf
respectively. - Breaking Change:
asArrayRecursive
andasOptArrayRecursive
have been renamed toasArrayOf
andasOptArrayOf
respectively. - Breaking Change: rename
TypeAssert
toTypeCheck
.
2.1.0
- Add:
makeStrictPartial
for convertingPartial<T>
toStrictPartial
. - Add: types
StrictPartial
andFuzzyPartial
, variants of the inbuiltPartial
type. - Add: type
StrictRequired
, variant ofRequired
.
2.1.1
- Fix: incorrect constraint on
makeStrictPartial
prevented passing in non-indexable instances.
2.2.0
- Add:
assertDefined
throws if the passed value isNullish
. - Add:
TypeAssertion
error class thrown by TypeAsserts.
2.2.1
- Fix: update sub-dependency to resolve npm advisory 1654
2.2.2
- Fix:
asInstance
,asOptInstance
,isInstance
andisOptInstance
were not exported from the package.
2.3.0
- Change: build target to ES2018 instead of ES3.
2.4.0
- Add:
invariant
function to assist type assertion - Add: JSON assertion functions
- Add: Basic type assertion functions
- Add: Literal type casts and checks
- Add: Primitive type, casts and checks
- Documentation: Correct some typos in the
isStruct
/asStruct
examples
2.5.0
- Add:
inspectType
function to describe the type of a value - Fix:
makeNumber
no longer returns a number strings prefixed with a number - Change: Type error messages now use the more descriptive
inspectType
instead oftypeof
for erroneous values
2.6.0
- Add:
asGuardedFunction
to wrap a function with parameter/return value type checks - Change: Depreciate the fallback parameter for all TypeCasts
- Documentation: Simplify the contents into primary headings only
- Change: Instead of being a custom subclass of Error TypeAssertion is now an alias to TypeError