import {DateTime, IANAZone} from "luxon"; export type NullableType = "function"|"string"|"object"|"number"|"bigint"|"boolean"|"undefined"|"symbol"|"null"|"array" export function nullableType(x: unknown): NullableType { return x === null ? "null" : Array.isArray(x) ? "array" : typeof x } export function assertType(x:unknown, expected: "array", context?: string): x is readonly unknown[] export function assertType(x:unknown, expected: "null", context?: string): x is null export function assertType(x:unknown, expected: "symbol", context?: string): x is symbol export function assertType(x:unknown, expected: "undefined", context?: string): x is undefined export function assertType(x:unknown, expected: "boolean", context?: string): x is boolean export function assertType(x:unknown, expected: "bigint", context?: string): x is bigint export function assertType(x:unknown, expected: "number", context?: string): x is number export function assertType(x:unknown, expected: "object", context?: string): x is object export function assertType(x:unknown, expected: "string", context?: string): x is string export function assertType(x:unknown, expected: "function", context?: string): x is Function export function assertType(x: unknown, expected: NullableType, context?: string): x is Function|string|object|number|bigint|boolean|undefined|symbol|null|readonly unknown[] { const actual = nullableType(x) if (expected !== actual) { console.log(`expected ${expected}, was ${actual} ${context ? `(in ${context})` : ""}`) return false } else { return true } } export function isObject(x: unknown, context?: string): x is object { return assertType(x, "object", context) } export function isArray(x: unknown, context?: string): x is readonly unknown[] { return assertType(x, "array", context) } export function isString(x: unknown, context?: string): x is string { return assertType(x, "string", context) } export function asString(x: unknown, context?: string): string|null { if (!isString(x, context)) { return null } return x } export function isNumber(x: unknown, context?: string): x is number { return assertType(x, "number", context) } export function asNumber(x: unknown, context?: string): number|null { if (!isNumber(x, context)) { return null } return x } export function isDate(x: unknown, context?: string): x is string { return asDate(x, context) !== null } export function asDate(x: unknown, context?: string): DateTime|null { if (!isString(x, context)) { return null } const result = DateTime.fromFormat(x, "EEEE', 'MMMM' 'd', 'yyyy' at 'h:mm:ss' 'a' 'z", {zone: IANAZone.create("UTC"), setZone: true, locale: "en-US"}) if (!result.isValid) { return null } return result } export function assertField(x: object, field: T, context?: string): x is object & {[ key in T ]: unknown} { if (!(field in x)) { console.log(`expected ${String(field)} not found${context ? ` (in ${context})` : ""}`) return false } return true } export function assertNoExtraFields(x: object, known: Set, context?: string): boolean { const unknown = Object.keys(x).filter(key => !known.has(key)) if (unknown.length > 1) { console.log(`unknown fields ${unknown.join(", ")}${context ? ` (in ${context})` : ""}`) return false } else if (unknown.length > 0) { console.log(`unknown field ${unknown[0]}${context ? ` (in ${context})` : ""}`) return false } return true }