export default class Assert { public static isUndefined(name: string, value: unknown): asserts value is undefined { if (value !== undefined) { throw new Error(`[${name}] must be undefined instead received [${typeof value}]`); } } public static isNull(name: string, value: unknown): asserts value is null { if (value !== null) { throw new Error(`[${name}] must be null instead received [${typeof value}]`); } } public static isDefined(name: string, value: unknown): asserts value is NonNullable { if (value === undefined || value === null) { throw new Error(`[${name}] must be defined instead received [${typeof value}]`); } } public static isBoolean(name: string, value: unknown): asserts value is boolean { if (!Assert._isBoolean(value)) { throw new Error(`[${name}] must be a boolean instead received [${typeof value}]`); } } public static isTrue(name: string, value: unknown): asserts value is true { Assert.isBoolean(name, value); if (!value) { throw new Error(`[${name}] must be true`); } } public static isString(name: string, value: unknown): asserts value is string { if (!Assert._isString(value)) { throw new Error(`[${name}] must be a string instead received [${typeof value}]`); } } public static isNonEmptyString(name: string, value: unknown): asserts value is string { if (!Assert._isNonEmptyString(value)) { throw new Error(`[${name}] must be a non-empty string instead received [${typeof value}]`); } } public static isNumber(name: string, value: unknown): asserts value is number { if (!Assert._isNumber(value)) { throw new Error(`[${name}] must be a number instead received [${typeof value}]`); } } public static isInteger(name: string, value: unknown): asserts value is number { Assert.isNumber(name, value); if (!Number.isInteger(value)) { throw new Error(`[${name}] must be an integer, received [${value}]`); } } public static isPositiveNumber(name: string, value: unknown): asserts value is number { if (!Assert._isNumberPositive(value)) { throw new Error(`[${name}] must be a positive number instead received [${typeof value}]`); } } public static isNumberInRange(name: string, lowerBound: number, upperBound: number, value: unknown): asserts value is number { if (upperBound < lowerBound) { throw new Error(`Invalid Range: [${name}] bounds are invalid, lower bound [${lowerBound}] must be less than upper bound [${upperBound}]`); } if (!(Assert._isNumber(value) && Assert._isNumberInRange(value, lowerBound, upperBound))) { throw new Error(`[${name}] must have a value between [${lowerBound}] and [${upperBound}] instead received [${value}]`); } } public static isFunction(name: string, value: unknown): asserts value is (...args: unknown[]) => unknown { if (typeof value !== 'function') { throw new Error(`[${name}] must be a function, instead received [${typeof value}]`); } } public static satisfiesInterface(name: string, obj: unknown, requiredProps: (keyof T)[]): asserts obj is T { Assert.isObject(name, obj); for (const prop of requiredProps) { if (!(prop in (obj as Record))) { throw new Error(`[${name}] missing required property: ${String(prop)}`); } } } public static isArray(name: string, value: unknown): asserts value is unknown[] { if (!Array.isArray(value)) { throw new Error(`[${name}] must be an array, instead received [${typeof value}]`); } } public static isArrayOf(name: string, arrayValueType: T, value: unknown): asserts value is T[] { Assert.isArray(name, value); for (const item of value) { const itemTypeof = typeof item; if (itemTypeof !== arrayValueType) { throw new Error(`[${name}] must be an array of [${arrayValueType}] received [${itemTypeof}]`); } } } public static isStringArray(name: string, value: unknown): asserts value is string[] { if (!Array.isArray(value)) { throw new Error(`[${name}] must be an array, instead received [${typeof value}]`); } for (const item of value) { Assert.isString(name, item); } } public static isObject(name: string, value: unknown): asserts value is T { if (value === null || typeof value !== 'object') { throw new Error(`[${name}] must be an object, instead received [${typeof value}]`); } } public static isEnumMember(name: string, enumObj: T, value: unknown): asserts value is T[keyof T] { if (!Object.values(enumObj).includes(value as T[keyof T])) { throw new Error(`[${name}] is not a member of the enum`); } } // eslint-disable-next-line public static isInstance(name: string, parentClass: new (...args: any[]) => T, object: unknown): asserts object is T { if (object === null || object === undefined || typeof object !== 'object') { throw new Error(`[${name}] must be an instance of [${parentClass.constructor.name}], instead received [${typeof object}]`); } if (!(object instanceof parentClass)) { throw new Error(`[${name}] must be an instance of [${parentClass.constructor.name}], instead received [${object.constructor.name}]`); } } private static _isBoolean(value: unknown): value is boolean { return typeof value === 'boolean'; } private static _isString(value: unknown): value is string { return typeof value === 'string'; } private static _isNonEmptyString(value: unknown): value is string { return Assert._isString(value) && value.length > 0; } private static _isNumber(value: unknown): value is number { return typeof value === 'number'; } private static _isNumberPositive(value: unknown): value is number { return Assert._isNumber(value) && value > 0; } private static _isNumberInRange(value: unknown, lowerBound: number, upperBound: number): value is number { return Assert._isNumber(value) && value >= lowerBound && value <= upperBound; } }