mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-05-10 01:27:48 +02:00
131 lines
4.7 KiB
TypeScript
131 lines
4.7 KiB
TypeScript
import type { SaveData, 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". */
|
|
export 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);
|
|
}
|
|
}
|
|
|
|
export function assertNumberArray(unknownData: unknown, assertFinite = false): asserts unknownData is number[] {
|
|
assertArray(unknownData);
|
|
for (const value of unknownData) {
|
|
if (assertFinite) {
|
|
if (!Number.isFinite(value)) {
|
|
console.error("The array contains a value that is not a finite number. Array:", unknownData);
|
|
throw new Error(`${value} is not a number.`);
|
|
}
|
|
} else {
|
|
if (typeof value !== "number") {
|
|
console.error("The array contains a value that is not a number. Array:", unknownData);
|
|
throw new Error(`${value} is not a number.`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export function isSaveData(unknownData: unknown): unknownData is SaveData {
|
|
if (typeof unknownData === "string") {
|
|
return true;
|
|
}
|
|
|
|
return unknownData instanceof Uint8Array && unknownData.buffer instanceof ArrayBuffer;
|
|
}
|
|
|
|
export function assertSaveData(unknownData: unknown): asserts unknownData is SaveData {
|
|
if (typeof unknownData !== "string" && !(unknownData instanceof Uint8Array)) {
|
|
console.error(unknownData);
|
|
throw new Error(`Invalid save data. Its type is ${getFriendlyType(unknownData)}.`);
|
|
}
|
|
if (unknownData instanceof Uint8Array && !(unknownData.buffer instanceof ArrayBuffer)) {
|
|
console.error(unknownData);
|
|
throw new Error("Invalid save data. It's Uint8Array, but its buffer is not ArrayBuffer.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function only narrows down the type to "number" at compile time, but it guarantees the value is a finite number
|
|
* at runtime.
|
|
*/
|
|
export function assertFiniteNumber(v: unknown): asserts v is number {
|
|
if (!Number.isFinite(v)) {
|
|
console.error("The value is not a finite number. Value:", v);
|
|
const type = getFriendlyType(v);
|
|
throw new TypeAssertionError(`The value is not a finite number. Its type is ${type}.`, type);
|
|
}
|
|
}
|
|
|
|
export function assertNonNullish<T>(v: unknown): asserts v is NonNullable<T> {
|
|
if (v === null || v === undefined) {
|
|
console.error("The value is nullish. Value:", v);
|
|
const type = getFriendlyType(v);
|
|
throw new TypeAssertionError(`The value is nullish. Its type is ${type}.`, type);
|
|
}
|
|
}
|