mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 06:18:42 +02:00
CODEBASE: Merge TypeAssertion files (#1922)
This commit is contained in:
@@ -51,7 +51,7 @@ import { Sleeve } from "../PersonObjects/Sleeve/Sleeve";
|
||||
import { autoCompleteTypeShorthand } from "./utils/terminalShorthands";
|
||||
import { resolveTeamCasualties, type OperationTeam } from "./Actions/TeamCasualties";
|
||||
import { shuffleArray } from "../Infiltration/ui/BribeGame";
|
||||
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||
import { assertObject } from "../utils/TypeAssertion";
|
||||
import { throwIfReachable } from "../utils/helpers/throwIfReachable";
|
||||
import { loadActionIdentifier } from "./utils/loadActionIdentifier";
|
||||
|
||||
@@ -1424,7 +1424,7 @@ export class Bladeburner implements OperationTeam {
|
||||
|
||||
/** Initializes a Bladeburner object from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): Bladeburner {
|
||||
objectAssert(value.data);
|
||||
assertObject(value.data);
|
||||
// operations and contracts are not loaded directly from the save, we load them in using a different method
|
||||
const contractsData = value.data.contracts;
|
||||
const operationsData = value.data.operations;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { codingContractTypesMetadata } from "./data/codingcontracttypes";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "./utils/JSONReviver";
|
||||
import { CodingContractEvent } from "./ui/React/CodingContractModal";
|
||||
import { ContractFilePath, resolveContractFilePath } from "./Paths/ContractFilePath";
|
||||
import { objectAssert } from "./utils/helpers/typeAssertion";
|
||||
import { assertObject } from "./utils/TypeAssertion";
|
||||
|
||||
/* Contract Types */
|
||||
export const CodingContractTypes = Object.fromEntries(codingContractTypesMetadata.map((x) => [x.name, x]));
|
||||
@@ -128,7 +128,7 @@ export class CodingContract {
|
||||
|
||||
/** Initializes a CodingContract from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): CodingContract {
|
||||
objectAssert(value.data);
|
||||
assertObject(value.data);
|
||||
// In previous versions, there was a data field instead of a state field.
|
||||
if ("data" in value.data) {
|
||||
value.data.state = value.data.data;
|
||||
|
||||
@@ -17,7 +17,13 @@ export function basicErrorMessage(ws: WorkerScript | ScriptDeath, msg: string, t
|
||||
return `${type} ERROR\n${ws.name}@${ws.hostname} (PID - ${ws.pid})\n\n${msg}`;
|
||||
}
|
||||
|
||||
/** Creates an error message string with a stack trace. */
|
||||
/**
|
||||
* Creates an error message string with a stack trace.
|
||||
*
|
||||
* When the player provides invalid input, we try to provide a stack trace that points to the player's invalid caller,
|
||||
* but we don't have an error instance with a stack trace. In order to get that stack trace, we create a new error
|
||||
* instance, then remove "unrelated" traces (code in our codebase) and leave only traces of the player's code.
|
||||
*/
|
||||
export function errorMessage(ctx: NetscriptContext, msg: string, type = "RUNTIME"): string {
|
||||
const errstack = new Error().stack;
|
||||
if (errstack === undefined) throw new Error("how did we not throw an error?");
|
||||
|
||||
@@ -56,7 +56,7 @@ import { hasScriptExtension, ScriptFilePath } from "../Paths/ScriptFilePath";
|
||||
import { CustomBoundary } from "../ui/Components/CustomBoundary";
|
||||
import { ServerConstants } from "../Server/data/Constants";
|
||||
import { basicErrorMessage, errorMessage, log } from "./ErrorMessages";
|
||||
import { assertString, debugType } from "./TypeAssertion";
|
||||
import { assertStringWithNSContext, debugType } from "./TypeAssertion";
|
||||
import {
|
||||
canAccessBitNodeFeature,
|
||||
getDefaultBitNodeOptions,
|
||||
@@ -124,7 +124,7 @@ export interface CompleteHGWOptions {
|
||||
/** Convert a provided value v for argument argName to string. If it wasn't originally a string or number, throw. */
|
||||
function string(ctx: NetscriptContext, argName: string, v: unknown): string {
|
||||
if (typeof v === "number") v = v + ""; // cast to string;
|
||||
assertString(ctx, argName, v);
|
||||
assertStringWithNSContext(ctx, argName, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -642,7 +642,7 @@ function gangTask(ctx: NetscriptContext, t: unknown): GangMemberTask {
|
||||
}
|
||||
|
||||
export function filePath(ctx: NetscriptContext, argName: string, filename: unknown): FilePath {
|
||||
assertString(ctx, argName, filename);
|
||||
assertStringWithNSContext(ctx, argName, filename);
|
||||
const path = resolveFilePath(filename, ctx.workerScript.name);
|
||||
if (path) return path;
|
||||
throw errorMessage(ctx, `Invalid ${argName}, was not a valid path: ${filename}`);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WorkerScript } from "./WorkerScript";
|
||||
import type { WorkerScript } from "./WorkerScript";
|
||||
|
||||
/**
|
||||
* Script death marker.
|
||||
|
||||
@@ -23,10 +23,18 @@ export const debugType = (v: unknown): string => {
|
||||
return `Is of type '${typeof v}', value: ${userFriendlyString(v)}`;
|
||||
};
|
||||
|
||||
export function assertString(ctx: NetscriptContext, argName: string, v: unknown): asserts v is string {
|
||||
/**
|
||||
* This function should be used to assert strings provided by the player. It uses a specialized utility function that
|
||||
* provides a stack trace pointing to the player's invalid caller.
|
||||
*/
|
||||
export function assertStringWithNSContext(ctx: NetscriptContext, argName: string, v: unknown): asserts v is string {
|
||||
if (typeof v !== "string") throw errorMessage(ctx, `${argName} expected to be a string. ${debugType(v)}`, "TYPE");
|
||||
}
|
||||
|
||||
export function assertFunction(ctx: NetscriptContext, argName: string, v: unknown): asserts v is () => void {
|
||||
export function assertFunctionWithNSContext(
|
||||
ctx: NetscriptContext,
|
||||
argName: string,
|
||||
v: unknown,
|
||||
): asserts v is () => void {
|
||||
if (typeof v !== "function") throw errorMessage(ctx, `${argName} expected to be a function ${debugType(v)}`, "TYPE");
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ import { InternalAPI, setRemovedFunctions, NSProxy } from "./Netscript/APIWrappe
|
||||
import { INetscriptExtra } from "./NetscriptFunctions/Extra";
|
||||
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||
import { getBitNodeMultipliers } from "./BitNode/BitNode";
|
||||
import { assert, arrayAssert, stringAssert, objectAssert } from "./utils/helpers/typeAssertion";
|
||||
import { assert, assertArray, assertString, assertObject } from "./utils/TypeAssertion";
|
||||
import { escapeRegExp } from "lodash";
|
||||
import numeral from "numeral";
|
||||
import { clearPort, peekPort, portHandle, readPort, tryWritePort, writePort, nextPortWrite } from "./NetscriptPort";
|
||||
@@ -107,7 +107,7 @@ import { getRamCost } from "./Netscript/RamCostGenerator";
|
||||
import { getEnumHelper } from "./utils/EnumHelper";
|
||||
import { setDeprecatedProperties, deprecationWarning } from "./utils/DeprecationHelper";
|
||||
import { ServerConstants } from "./Server/data/Constants";
|
||||
import { assertFunction } from "./Netscript/TypeAssertion";
|
||||
import { assertFunctionWithNSContext } from "./Netscript/TypeAssertion";
|
||||
import { Router } from "./ui/GameRoot";
|
||||
import { Page } from "./ui/Router";
|
||||
import { canAccessBitNodeFeature, validBitNodes } from "./BitNode/BitNodeUtils";
|
||||
@@ -1694,11 +1694,11 @@ export const ns: InternalAPI<NSFull> = {
|
||||
const options: { type?: string; choices?: string[] } = {};
|
||||
_options ??= options;
|
||||
const txt = helpers.string(ctx, "txt", _txt);
|
||||
assert(_options, objectAssert, (type) =>
|
||||
assert(_options, assertObject, (type) =>
|
||||
helpers.errorMessage(ctx, `Invalid type for options: ${type}. Should be object.`, "TYPE"),
|
||||
);
|
||||
if (_options.type !== undefined) {
|
||||
assert(_options.type, stringAssert, (type) =>
|
||||
assert(_options.type, assertString, (type) =>
|
||||
helpers.errorMessage(ctx, `Invalid type for options.type: ${type}. Should be string.`, "TYPE"),
|
||||
);
|
||||
options.type = _options.type;
|
||||
@@ -1710,7 +1710,7 @@ export const ns: InternalAPI<NSFull> = {
|
||||
);
|
||||
}
|
||||
if (options.type === "select") {
|
||||
assert(_options.choices, arrayAssert, (type) =>
|
||||
assert(_options.choices, assertArray, (type) =>
|
||||
helpers.errorMessage(
|
||||
ctx,
|
||||
`Invalid type for options.choices: ${type}. If options.type is "select", options.choices must be an array.`,
|
||||
@@ -1811,7 +1811,7 @@ export const ns: InternalAPI<NSFull> = {
|
||||
}),
|
||||
atExit: (ctx) => (callback, _id) => {
|
||||
const id = _id ? helpers.string(ctx, "id", _id) : "default";
|
||||
assertFunction(ctx, "callback", callback);
|
||||
assertFunctionWithNSContext(ctx, "callback", callback);
|
||||
ctx.workerScript.atExit.set(id, callback);
|
||||
},
|
||||
mv: (ctx) => (_host, _source, _destination) => {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
|
||||
import { helpers } from "../Netscript/NetscriptHelpers";
|
||||
import { getEnumHelper } from "../utils/EnumHelper";
|
||||
import { Skills } from "../Bladeburner/data/Skills";
|
||||
import { assertString } from "../Netscript/TypeAssertion";
|
||||
import { assertStringWithNSContext } from "../Netscript/TypeAssertion";
|
||||
import { BlackOperations, blackOpsArray } from "../Bladeburner/data/BlackOperations";
|
||||
import { checkSleeveAPIAccess, checkSleeveNumber } from "../NetscriptFunctions/Sleeve";
|
||||
import { canAccessBitNodeFeature } from "../BitNode/BitNodeUtils";
|
||||
@@ -37,8 +37,8 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
|
||||
};
|
||||
function getAction(ctx: NetscriptContext, type: unknown, name: unknown): Action {
|
||||
const bladeburner = Player.bladeburner;
|
||||
assertString(ctx, "type", type);
|
||||
assertString(ctx, "name", name);
|
||||
assertStringWithNSContext(ctx, "type", type);
|
||||
assertStringWithNSContext(ctx, "name", name);
|
||||
if (bladeburner === null) throw new Error("Must have joined bladeburner");
|
||||
const action = bladeburner.getActionFromTypeAndName(type, name);
|
||||
if (!action) throw helpers.errorMessage(ctx, `Invalid action type='${type}', name='${name}'`);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { scaleWorkStats } from "../../../Work/WorkStats";
|
||||
import { getKeyList } from "../../../utils/helpers/getKeyList";
|
||||
import { loadActionIdentifier } from "../../../Bladeburner/utils/loadActionIdentifier";
|
||||
import { invalidWork } from "../../../Work/InvalidWork";
|
||||
import { objectAssert } from "../../../utils/helpers/typeAssertion";
|
||||
import { assertObject } from "../../../utils/TypeAssertion";
|
||||
|
||||
interface SleeveBladeburnerWorkParams {
|
||||
actionId: ActionIdentifier & { type: BladeburnerActionType.General | BladeburnerActionType.Contract };
|
||||
@@ -99,7 +99,7 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
|
||||
|
||||
/** Initializes a BladeburnerWork object from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): SleeveBladeburnerWork {
|
||||
objectAssert(value.data);
|
||||
assertObject(value.data);
|
||||
let actionId = loadActionIdentifier(value.data?.actionId);
|
||||
if (!actionId) {
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Sleeve } from "../Sleeve";
|
||||
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
|
||||
import { Locations } from "../../../Locations/Locations";
|
||||
import { isMember } from "../../../utils/EnumHelper";
|
||||
import { objectAssert } from "../../../utils/helpers/typeAssertion";
|
||||
import { assertObject } from "../../../utils/TypeAssertion";
|
||||
|
||||
export const isSleeveClassWork = (w: SleeveWorkClass | null): w is SleeveClassWork =>
|
||||
w !== null && w.type === SleeveWorkType.CLASS;
|
||||
@@ -55,7 +55,7 @@ export class SleeveClassWork extends SleeveWorkClass {
|
||||
|
||||
/** Initializes a ClassWork object from a JSON save state. */
|
||||
static fromJSON(value: IReviverValue): SleeveClassWork {
|
||||
objectAssert(value.data);
|
||||
assertObject(value.data);
|
||||
if (typeof value.data.classType !== "string" || !(value.data.classType in Classes)) {
|
||||
value.data.classType = "Computer Science";
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import { SaveDataError, canUseBinaryFormat, decodeSaveData, encodeJsonSaveString
|
||||
import { isBinaryFormat } from "../electron/saveDataBinaryFormat";
|
||||
import { downloadContentAsFile } from "./utils/FileUtils";
|
||||
import { handleGetSaveDataInfoError } from "./utils/ErrorHandler";
|
||||
import { isObject, objectAssert } from "./utils/helpers/typeAssertion";
|
||||
import { isObject, assertObject } from "./utils/TypeAssertion";
|
||||
import { evaluateVersionCompatibility } from "./utils/SaveDataMigrationUtils";
|
||||
import { Reviver } from "./utils/GenericReviver";
|
||||
|
||||
@@ -99,7 +99,7 @@ export type BitburnerSaveObjectType = {
|
||||
* If saveObject has these properties, we check if their values are strings.
|
||||
*/
|
||||
function assertBitburnerSaveObjectType(saveObject: unknown): asserts saveObject is BitburnerSaveObjectType {
|
||||
objectAssert(saveObject);
|
||||
assertObject(saveObject);
|
||||
|
||||
const mandatoryKeysOfSaveObj = [
|
||||
"PlayerSave",
|
||||
|
||||
@@ -13,7 +13,7 @@ import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
|
||||
import { IPAddress, isIPAddress } from "../Types/strings";
|
||||
|
||||
import "../Script/RunningScript"; // For reviver side-effect
|
||||
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||
import { assertObject } from "../utils/TypeAssertion";
|
||||
|
||||
/**
|
||||
* Map of all Servers that exist in the game
|
||||
@@ -218,7 +218,7 @@ export function prestigeAllServers(): void {
|
||||
|
||||
export function loadAllServers(saveString: string): void {
|
||||
const allServersData: unknown = JSON.parse(saveString, Reviver);
|
||||
objectAssert(allServersData);
|
||||
assertObject(allServersData);
|
||||
for (const [serverName, server] of Object.entries(allServersData)) {
|
||||
if (!(server instanceof Server) && !(server instanceof HacknetServer)) {
|
||||
throw new Error(`Server ${serverName} is not an instance of Server or HacknetServer.`);
|
||||
|
||||
@@ -24,7 +24,7 @@ import lodash from "lodash";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
|
||||
import type { ScriptKey } from "../utils/helpers/scriptKey";
|
||||
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||
import { assertObject } from "../utils/TypeAssertion";
|
||||
import { clampNumber } from "../utils/helpers/clampNumber";
|
||||
|
||||
interface IConstructorParams {
|
||||
@@ -295,7 +295,7 @@ export abstract class BaseServer implements IServer {
|
||||
// RunningScripts are stored as a simple array, both for backward compatibility,
|
||||
// compactness, and ease of filtering them here.
|
||||
const result = Generic_toJSON(ctorName, this, keys);
|
||||
objectAssert(result.data);
|
||||
assertObject(result.data);
|
||||
if (Settings.ExcludeRunningScriptsFromSave) {
|
||||
result.data.runningScripts = [];
|
||||
return result;
|
||||
@@ -316,7 +316,7 @@ export abstract class BaseServer implements IServer {
|
||||
// Initializes a Server Object from a JSON save state
|
||||
// Called by subclasses, not Reviver.
|
||||
static fromJSONBase<T extends BaseServer>(value: IReviverValue, ctor: new () => T, keys: readonly (keyof T)[]): T {
|
||||
objectAssert(value.data);
|
||||
assertObject(value.data);
|
||||
const server = Generic_fromJSON(ctor, value.data, keys);
|
||||
if (value.data.runningScripts != null && Array.isArray(value.data.runningScripts)) {
|
||||
server.savedScripts = value.data.runningScripts;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { defaultTheme } from "../Themes/Themes";
|
||||
import { defaultStyles } from "../Themes/Styles";
|
||||
import { CursorStyle, CursorBlinking, WordWrapOptions } from "../ScriptEditor/ui/Options";
|
||||
import { defaultMonacoTheme } from "../ScriptEditor/ui/themes";
|
||||
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||
import { assertObject } from "../utils/TypeAssertion";
|
||||
import { Result } from "../types";
|
||||
import {
|
||||
assertAndSanitizeEditorTheme,
|
||||
@@ -186,7 +186,7 @@ export const Settings = {
|
||||
|
||||
load(saveString: string) {
|
||||
const save: unknown = JSON.parse(saveString);
|
||||
objectAssert(save);
|
||||
assertObject(save);
|
||||
save.overview && Object.assign(Settings.overview, save.overview);
|
||||
try {
|
||||
// Sanitize theme data. Invalid theme data may crash the game or make it stuck in the loading page.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { arrayAssert } from "../utils/helpers/typeAssertion";
|
||||
import { assertArray } from "../utils/TypeAssertion";
|
||||
import type { IReviverValue } from "../utils/JSONReviver";
|
||||
// Versions of js builtin classes that can be converted to and from JSON for use in save files
|
||||
|
||||
@@ -7,7 +7,7 @@ export class JSONSet<T> extends Set<T> {
|
||||
return { ctor: "JSONSet", data: Array.from(this) };
|
||||
}
|
||||
static fromJSON(value: IReviverValue): JSONSet<any> {
|
||||
arrayAssert(value.data);
|
||||
assertArray(value.data);
|
||||
return new JSONSet(value.data);
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,9 @@ export class JSONMap<K, __V> extends Map<K, __V> {
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): JSONMap<any, any> {
|
||||
arrayAssert(value.data);
|
||||
assertArray(value.data);
|
||||
for (const item of value.data) {
|
||||
arrayAssert(item);
|
||||
assertArray(item);
|
||||
if (item.length !== 2) {
|
||||
console.error("Invalid data passed to JSONMap.fromJSON(). Value:", value);
|
||||
throw new Error(`An item is not an array with exactly 2 items. Its length is ${item.length}.`);
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Member } from "../types";
|
||||
import type { NetscriptContext } from "../Netscript/APIWrapper";
|
||||
|
||||
import * as allEnums from "../Enums";
|
||||
import { assertString } from "../Netscript/TypeAssertion";
|
||||
import { assertStringWithNSContext } from "../Netscript/TypeAssertion";
|
||||
import { errorMessage } from "../Netscript/ErrorMessages";
|
||||
import { getRandomIntInclusive } from "./helpers/getRandomIntInclusive";
|
||||
import { getRecordValues } from "../Types/Record";
|
||||
@@ -43,7 +43,7 @@ class EnumHelper<EnumObj extends object, EnumMember extends Member<EnumObj> & st
|
||||
if (match) return match;
|
||||
|
||||
// No match found, create error message
|
||||
assertString(ctx, argName, toValidate);
|
||||
assertStringWithNSContext(ctx, argName, toValidate);
|
||||
let allowableValues = `Allowable values: ${this.valueArray.map((val) => `"${val}"`).join(", ")}`;
|
||||
// Don't display all possibilities for large enums
|
||||
if (this.valueArray.length > 10) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */
|
||||
import { ObjectValidator } from "./Validator";
|
||||
import { JSONMap, JSONSet } from "../Types/Jsonable";
|
||||
import { objectAssert } from "./helpers/typeAssertion";
|
||||
import { assertObject } from "./TypeAssertion";
|
||||
|
||||
type JsonableClass = (new () => { toJSON: () => IReviverValue }) & {
|
||||
fromJSON: (value: IReviverValue) => unknown;
|
||||
@@ -58,7 +58,7 @@ export function Generic_fromJSON<T extends Record<string, any>>(
|
||||
data: unknown,
|
||||
keys?: readonly (keyof T)[],
|
||||
): T {
|
||||
objectAssert(data);
|
||||
assertObject(data);
|
||||
const obj = new ctor();
|
||||
// If keys were provided, just load the provided keys (if they are in the data)
|
||||
if (keys) {
|
||||
|
||||
@@ -1,4 +1,74 @@
|
||||
import type { Unknownify } from "../types";
|
||||
|
||||
// This function is empty because Unknownify<T> is a typesafe assertion on any object with no runtime checks needed.
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
export function assertLoadingType<T extends object>(val: object): asserts val is Unknownify<T> {}
|
||||
|
||||
export class TypeAssertionError extends Error {
|
||||
friendlyType: string;
|
||||
|
||||
constructor(message: string, friendlyType: string, options?: ErrorOptions) {
|
||||
super(message, options);
|
||||
this.name = this.constructor.name;
|
||||
this.friendlyType = friendlyType;
|
||||
}
|
||||
}
|
||||
|
||||
/** Function for providing custom error message to throw for a type assertion.
|
||||
* @param v: Value to assert type of
|
||||
* @param assertFn: Typechecking function to use for asserting type of v.
|
||||
* @param msgFn: Function to use to generate an error message if an error is produced. */
|
||||
export function assert<T>(
|
||||
v: unknown,
|
||||
assertFn: (v: unknown) => asserts v is T,
|
||||
msgFn: (type: string) => string,
|
||||
): asserts v is T {
|
||||
try {
|
||||
assertFn(v);
|
||||
} catch (e) {
|
||||
if (e instanceof TypeAssertionError) {
|
||||
throw msgFn(e.friendlyType);
|
||||
}
|
||||
const type = typeof e === "string" ? e : "unknown";
|
||||
throw msgFn(type);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the friendlyType of v. arrays are "array" and null is "null". */
|
||||
function getFriendlyType(v: unknown): string {
|
||||
return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
||||
}
|
||||
|
||||
export function isObject(v: unknown): v is Record<string, unknown> {
|
||||
return getFriendlyType(v) === "object";
|
||||
}
|
||||
|
||||
/** For non-objects, and for array/null, throws an error with the friendlyType of v. */
|
||||
export function assertObject(v: unknown): asserts v is Record<string, unknown> {
|
||||
const type = getFriendlyType(v);
|
||||
if (type !== "object") {
|
||||
console.error("The value is not an object. Value:", v);
|
||||
throw new TypeAssertionError(
|
||||
`The value is not an object. Its type is ${type}. Its string value is ${String(v)}.`,
|
||||
type,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** For non-string, throws an error with the friendlyType of v. */
|
||||
export function assertString(v: unknown): asserts v is string {
|
||||
const type = getFriendlyType(v);
|
||||
if (type !== "string") {
|
||||
console.error("The value is not a string. Value:", v);
|
||||
throw new TypeAssertionError(`The value is not an string. Its type is ${type}.`, type);
|
||||
}
|
||||
}
|
||||
|
||||
/** For non-array, throws an error with the friendlyType of v. */
|
||||
export function assertArray(v: unknown): asserts v is unknown[] {
|
||||
if (!Array.isArray(v)) {
|
||||
console.error("The value is not an array. Value:", v);
|
||||
const type = getFriendlyType(v);
|
||||
throw new TypeAssertionError(`The value is not an array. Its type is ${type}.`, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// Various functions for asserting types.
|
||||
|
||||
export class TypeAssertionError extends Error {
|
||||
friendlyType: string;
|
||||
|
||||
constructor(message: string, friendlyType: string, options?: ErrorOptions) {
|
||||
super(message, options);
|
||||
this.name = this.constructor.name;
|
||||
this.friendlyType = friendlyType;
|
||||
}
|
||||
}
|
||||
|
||||
/** Function for providing custom error message to throw for a type assertion.
|
||||
* @param v: Value to assert type of
|
||||
* @param assertFn: Typechecking function to use for asserting type of v.
|
||||
* @param msgFn: Function to use to generate an error message if an error is produced. */
|
||||
export function assert<T>(
|
||||
v: unknown,
|
||||
assertFn: (v: unknown) => asserts v is T,
|
||||
msgFn: (type: string) => string,
|
||||
): asserts v is T {
|
||||
try {
|
||||
assertFn(v);
|
||||
} catch (e) {
|
||||
if (e instanceof TypeAssertionError) {
|
||||
throw msgFn(e.friendlyType);
|
||||
}
|
||||
const type = typeof e === "string" ? e : "unknown";
|
||||
throw msgFn(type);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the friendlyType of v. arrays are "array" and null is "null". */
|
||||
function getFriendlyType(v: unknown): string {
|
||||
return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
||||
}
|
||||
|
||||
export function isObject(v: unknown): v is Record<string, unknown> {
|
||||
return getFriendlyType(v) === "object";
|
||||
}
|
||||
|
||||
/** For non-objects, and for array/null, throws an error with the friendlyType of v. */
|
||||
export function objectAssert(v: unknown): asserts v is Record<string, unknown> {
|
||||
const type = getFriendlyType(v);
|
||||
if (type !== "object") {
|
||||
console.error("The value is not an object. Value:", v);
|
||||
throw new TypeAssertionError(
|
||||
`The value is not an object. Its type is ${type}. Its string value is ${String(v)}.`,
|
||||
type,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** For non-string, throws an error with the friendlyType of v. */
|
||||
export function stringAssert(v: unknown): asserts v is string {
|
||||
const type = getFriendlyType(v);
|
||||
if (type !== "string") {
|
||||
console.error("The value is not a string. Value:", v);
|
||||
throw new TypeAssertionError(`The value is not an string. Its type is ${type}.`, type);
|
||||
}
|
||||
}
|
||||
|
||||
/** For non-array, throws an error with the friendlyType of v. */
|
||||
export function arrayAssert(v: unknown): asserts v is unknown[] {
|
||||
if (!Array.isArray(v)) {
|
||||
console.error("The value is not an array. Value:", v);
|
||||
const type = getFriendlyType(v);
|
||||
throw new TypeAssertionError(`The value is not an array. Its type is ${type}.`, type);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user