CODEBASE: Fix lint errors 2 (#1756)

This commit is contained in:
catloversg 2024-11-07 14:09:11 +07:00 committed by GitHub
parent e3c10e9f0f
commit 36c143b687
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 267 additions and 146 deletions

@ -35,6 +35,7 @@ module.exports = {
"@typescript-eslint/no-explicit-any": "off",
"react/no-unescaped-entities": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/no-unsafe-enum-comparison": "off",
},
settings: {
react: {

@ -49,3 +49,14 @@ declare global {
}
}
}
module "monaco-vim" {
export const initVimMode: (...args: unknown[]) => { dispose: () => void };
export const VimMode: {
Vim: {
defineEx: (...args: unknown[]) => void;
mapCommand: (...args: unknown[]) => void;
defineAction: (...args: unknown[]) => void;
};
};
}

@ -3,6 +3,7 @@ import { Card, Suit } from "./Card";
import { makeStyles } from "tss-react/mui";
import Paper from "@mui/material/Paper";
import { throwIfReachable } from "../../utils/helpers/throwIfReachable";
interface Props {
card: Card;
@ -51,7 +52,7 @@ export const ReactCard: FC<Props> = ({ card, hidden }) => {
suit = <span>&#9824;</span>;
break;
default:
throw new Error(`MissingCaseException: ${card.suit}`);
throwIfReachable(card.suit);
}
return (
<Paper className={`${classes.card} ${card.isRedSuit() ? classes.red : classes.black}`}>

@ -2,13 +2,15 @@ import { Company } from "./Company";
import { CompanyPosition } from "./CompanyPosition";
import { PlayerCondition, haveSkill, haveCompanyRep } from "../Faction/FactionJoinCondition";
import type { Skills } from "../PersonObjects/Skills";
import { getRecordEntries } from "../Types/Record";
export function getJobRequirements(company: Company, pos: CompanyPosition): PlayerCondition[] {
const reqSkills = pos.requiredSkills(company.jobStatReqOffset);
const reqs = [];
for (const [skillName, value] of Object.entries(reqSkills)) {
if (value > 0) reqs.push(haveSkill(skillName as keyof Skills, value));
for (const [skillName, value] of getRecordEntries(reqSkills)) {
if (value > 0) {
reqs.push(haveSkill(skillName, value));
}
}
if (pos.requiredReputation > 0) {
reqs.push(haveCompanyRep(company.name, pos.requiredReputation));

@ -17,7 +17,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { JSONMap, JSONSet } from "../Types/Jsonable";
import { formatMoney } from "../ui/formatNumber";
import { isPositiveInteger } from "../types";
import { isPositiveInteger, type Result } from "../types";
import { createEnumKeyedRecord, getRecordValues } from "../Types/Record";
import { getKeyList } from "../utils/helpers/getKeyList";
@ -372,29 +372,56 @@ export class Corporation {
}
}
/** Purchasing a one-time unlock
* @returns A string on failure, indicating the reason for failure. */
purchaseUnlock(unlockName: CorpUnlockName): string | void {
if (this.unlocks.has(unlockName)) return `The corporation has already unlocked ${unlockName}`;
/**
* Purchasing a one-time unlock
*/
purchaseUnlock(unlockName: CorpUnlockName): Result {
if (this.unlocks.has(unlockName)) {
return {
success: false,
message: `${unlockName} has already been unlocked.`,
};
}
const price = CorpUnlocks[unlockName].price;
if (this.funds < price) return `Insufficient funds to purchase ${unlockName}, requires ${formatMoney(price)}`;
if (this.funds < price) {
return {
success: false,
message: `Insufficient funds to purchase ${unlockName}, requires ${formatMoney(price)}.`,
};
}
this.loseFunds(price, "upgrades");
this.unlocks.add(unlockName);
// Apply effects for one-time unlocks
if (unlockName === CorpUnlockName.ShadyAccounting) this.dividendTax -= 0.05;
if (unlockName === CorpUnlockName.GovernmentPartnership) this.dividendTax -= 0.1;
if (unlockName === CorpUnlockName.ShadyAccounting) {
this.dividendTax -= 0.05;
}
if (unlockName === CorpUnlockName.GovernmentPartnership) {
this.dividendTax -= 0.1;
}
return {
success: true,
};
}
/** Purchasing a levelable upgrade
* @returns A string on failure, indicating the reason for failure. */
purchaseUpgrade(upgradeName: CorpUpgradeName, amount = 1): string | void {
/**
* Purchasing a levelable upgrade
*/
purchaseUpgrade(upgradeName: CorpUpgradeName, amount = 1): Result {
if (!isPositiveInteger(amount)) {
return `Number of upgrade levels purchased must be a positive integer (attempted: ${amount}).`;
return {
success: false,
message: `Number of upgrade levels purchased must be a positive integer (attempted: ${amount}).`,
};
}
const upgrade = CorpUpgrades[upgradeName];
const totalCost = calculateUpgradeCost(this, upgrade, amount);
if (this.funds < totalCost) return `Not enough funds to purchase ${amount} of upgrade ${upgradeName}.`;
if (this.funds < totalCost) {
return {
success: false,
message: `Not enough funds to purchase ${amount} of upgrade ${upgradeName}.`,
};
}
this.loseFunds(totalCost, "upgrades");
this.upgrades[upgradeName].level += amount;
this.upgrades[upgradeName].value += upgrade.benefit * amount;
@ -407,6 +434,9 @@ export class Corporation {
}
}
}
return {
success: true,
};
}
getProductionMultiplier(): number {

@ -16,6 +16,7 @@ import { PartialRecord, getRecordEntries, getRecordKeys, getRecordValues } from
import { Material } from "./Material";
import { getKeyList } from "../utils/helpers/getKeyList";
import { calculateMarkupMultiplier } from "./helpers";
import { throwIfReachable } from "../utils/helpers/throwIfReachable";
interface DivisionParams {
name: string;
@ -704,8 +705,7 @@ export class Division {
case "START":
break;
default:
console.error(`Invalid state: ${state}`);
break;
throwIfReachable(state);
} //End switch(this.state)
this.updateWarehouseSizeUsed(warehouse);
}
@ -936,8 +936,7 @@ export class Division {
case "EXPORT":
break;
default:
console.error(`Invalid State: ${state}`);
break;
throwIfReachable(state);
} //End switch(this.state)
this.updateWarehouseSizeUsed(warehouse);
}

@ -5,6 +5,7 @@ import { Division } from "./Division";
import { Corporation } from "./Corporation";
import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive";
import { createEnumKeyedRecord, getRecordKeys } from "../Types/Record";
import { throwIfReachable } from "../utils/helpers/throwIfReachable";
interface IParams {
city: CityName;
@ -165,8 +166,7 @@ export class OfficeSpace {
case "total":
continue;
default:
console.error(`Invalid employee position: ${name}`);
break;
throwIfReachable(name);
}
this.employeeProductionByJob[name] = this.employeeJobs[name] * prodMult * prodBase;
total += this.employeeProductionByJob[name];

@ -214,7 +214,9 @@ export class Product {
calculateRating(industry: Division): void {
const weights = IndustriesData[industry.type].product?.ratingWeights;
if (!weights) return console.error(`Could not find product rating weights for: ${industry}`);
if (!weights) {
return console.error(`Could not find product rating weights for: ${industry.name}`);
}
this.rating = getRecordEntries(weights).reduce(
(total, [statName, weight]) => total + this.stats[statName] * weight,
0,

@ -31,8 +31,10 @@ export function LevelableUpgrade({ upgradeName, mult, rerender }: IProps): React
const tooltip = data.desc;
function onClick(): void {
if (corp.funds < cost) return;
const message = corp.purchaseUpgrade(upgradeName, amount);
if (message) dialogBoxCreate(`Could not upgrade ${upgradeName} ${amount} times:\n${message}`);
const result = corp.purchaseUpgrade(upgradeName, amount);
if (!result.success) {
dialogBoxCreate(`Could not upgrade ${upgradeName} ${amount} times:\n${result.message}`);
}
rerender();
}

@ -25,8 +25,10 @@ export function Unlock(props: UnlockProps): React.ReactElement {
function onClick(): void {
// corp.unlock handles displaying a dialog on failure
const message = corp.purchaseUnlock(props.name);
if (message) dialogBoxCreate(`Error while attempting to purchase ${props.name}:\n${message}`);
const result = corp.purchaseUnlock(props.name);
if (!result.success) {
dialogBoxCreate(`Error while attempting to purchase ${props.name}:\n${result.message}`);
}
// Rerenders the parent, which should remove this item if the purchase was successful
props.rerender();
}

@ -44,8 +44,8 @@ export function SellSharesModal(props: IProps): React.ReactElement {
props.onClose();
props.rerender();
setShares(NaN);
} catch (err) {
dialogBoxCreate(`${err as Error}`);
} catch (error) {
dialogBoxCreate(String(error));
}
}

@ -7,17 +7,17 @@ export let staneksGift = new StaneksGift();
export function loadStaneksGift(saveString: string): void {
if (saveString) {
staneksGift = JSON.parse(saveString, Reviver);
staneksGift = JSON.parse(saveString, Reviver) as StaneksGift;
} else {
staneksGift = new StaneksGift();
}
}
export function zeros(width: number, height: number): number[][] {
const array = [];
const array: number[][] = [];
for (let i = 0; i < width; ++i) {
array.push(Array(height).fill(0));
array.push(Array<number>(height).fill(0));
}
return array;

@ -18,9 +18,6 @@ export class StaneksGift extends BaseGift {
isBonusCharging = false;
justCharged = true;
storedCycles = 0;
constructor() {
super();
}
baseSize(): number {
return StanekConstants.BaseSize + currentNodeMults.StaneksGiftExtraSize + Player.activeSourceFileLvl(13);

@ -155,13 +155,27 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
>
<Button onClick={startImport} startIcon={<Upload />} sx={{ gridArea: "import" }}>
Import Game
<input ref={importInput} id="import-game-file-selector" type="file" hidden onChange={onImport} />
<input
ref={importInput}
id="import-game-file-selector"
type="file"
hidden
onChange={(event) => {
onImport(event).catch((error) => {
console.error(error);
});
}}
/>
</Button>
</Tooltip>
<ConfirmationModal
open={importSaveOpen}
onClose={() => setImportSaveOpen(false)}
onConfirm={() => confirmedImportGame()}
onConfirm={() => {
confirmedImportGame().catch((error) => {
console.error(error);
});
}}
additionalButton={<Button onClick={compareSaveGame}>Compare Save</Button>}
confirmationText={
<>

@ -15,6 +15,7 @@ import { Settings } from "../../Settings/Settings";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { StatsRow } from "../../ui/React/StatsRow";
import { useStyles } from "../../ui/React/CharacterOverview";
import { getKeyFromReactElements } from "../../utils/StringHelperFunctions";
interface IProps {
member: GangMember;
@ -103,7 +104,7 @@ export function GangMemberStats(props: IProps): React.ReactElement {
</TableCell>
</TableRow>
{data.map(([a, b]) => (
<TableRow key={a.toString() + b.toString()}>
<TableRow key={getKeyFromReactElements(a, b)}>
<TableCell classes={{ root: classes.cellNone }}>
<Typography>{a}</Typography>
</TableCell>

@ -83,7 +83,11 @@ export function loadGo(data: unknown): boolean {
Go.storeCycles(loadStoredCycles(parsedData.storedCycles));
// If it's the AI's turn, initiate their turn, which will populate nextTurn
if (currentGame.previousPlayer === GoColor.black && currentGame.ai !== GoOpponent.none) makeAIMove(currentGame);
if (currentGame.previousPlayer === GoColor.black && currentGame.ai !== GoOpponent.none) {
makeAIMove(currentGame).catch((error) => {
showError(error);
});
}
// If it's not the AI's turn and we're not in gameover status, initialize nextTurn promise based on the previous move/pass
else if (currentGame.previousPlayer) {
const previousMove = getPreviousMove();

@ -135,7 +135,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
return (
<Typography
key={`${item}${uniqueId()}`}
key={uniqueId()}
sx={{
color: color,
border: `2px solid ${item.current ? Settings.theme.infolight : Settings.theme.primary}`,

@ -66,7 +66,7 @@ class NSProxyHandler<API extends GenericAPI<API>> {
const descriptor = Object.getOwnPropertyDescriptor(this.ns, key);
if (!descriptor) return descriptor;
const field = descriptor.value;
const field: unknown = descriptor.value;
if (typeof field === "function") {
const arrayPath = [...this.tree, key];
@ -74,7 +74,7 @@ class NSProxyHandler<API extends GenericAPI<API>> {
const ctx = { workerScript: this.ws, function: key, functionPath };
// Only do the context-binding once, instead of each time the function
// is called.
const func: any = field(ctx);
const func = field(ctx) as (...args: unknown[]) => unknown;
const wrappedFunction = function (...args: unknown[]): unknown {
// What remains *must* be called every time.
helpers.checkEnvFlags(ctx);

@ -692,8 +692,11 @@ function getRunningScript(ctx: NetscriptContext, ident: ScriptIdentifier): Runni
return findRunningScriptByPid(ident);
} else {
const scripts = getRunningScriptsByArgs(ctx, ident.scriptname, ident.hostname, ident.args);
if (scripts === null) return null;
return scripts.values().next().value ?? null;
if (scripts === null) {
return null;
}
const next = scripts.values().next();
return !next.done ? next.value : null;
}
}

@ -624,15 +624,19 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
checkAccess(ctx);
const unlockName = getEnumHelper("CorpUnlockName").nsGetMember(ctx, _unlockName, "unlockName");
const corporation = getCorporation();
const message = corporation.purchaseUnlock(unlockName);
if (message) throw new Error(`Could not unlock ${unlockName}: ${message}`);
const result = corporation.purchaseUnlock(unlockName);
if (!result.success) {
throw new Error(`Could not unlock ${unlockName}: ${result.message}`);
}
},
levelUpgrade: (ctx) => (_upgradeName) => {
checkAccess(ctx);
const upgradeName = getEnumHelper("CorpUpgradeName").nsGetMember(ctx, _upgradeName, "upgradeName");
const corporation = getCorporation();
const message = corporation.purchaseUpgrade(upgradeName, 1);
if (message) throw new Error(`Could not upgrade ${upgradeName}: ${message}`);
const result = corporation.purchaseUpgrade(upgradeName, 1);
if (!result.success) {
throw new Error(`Could not upgrade ${upgradeName}: ${result.message}`);
}
},
issueDividends: (ctx) => (_rate) => {
checkAccess(ctx);

@ -13,6 +13,7 @@ import { Factions } from "../Faction/Factions";
import { getEnumHelper } from "../utils/EnumHelper";
import { helpers } from "../Netscript/NetscriptHelpers";
import { filterTruthy } from "../utils/helpers/ArrayHelpers";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
export function NetscriptInfiltration(): InternalAPI<NetscriptInfiltation> {
const getLocationsWithInfiltrations = Object.values(Locations).filter(
@ -21,16 +22,29 @@ export function NetscriptInfiltration(): InternalAPI<NetscriptInfiltation> {
const calculateInfiltrationData = (ctx: NetscriptContext, locationName: LocationName): InfiltrationLocation => {
const location = Locations[locationName];
if (location === undefined) throw helpers.errorMessage(ctx, `Location '${location}' does not exists.`);
if (location.infiltrationData === undefined)
throw helpers.errorMessage(ctx, `Location '${location}' does not provide infiltrations.`);
if (location === undefined) {
throw helpers.errorMessage(ctx, `Location "${locationName}" does not exist.`);
}
if (location.infiltrationData === undefined) {
throw helpers.errorMessage(ctx, `Location "${locationName}" does not provide infiltrations.`);
}
const locationCity = location.city;
/**
* location.city is only null when the location is available in all cities. This kind of location does not have
* infiltration data.
*/
if (locationCity === null) {
const errorMessage = `Location "${locationName}" is available in all cities, but it still has infiltration data.`;
exceptionAlert(new Error(errorMessage));
throw helpers.errorMessage(ctx, errorMessage);
}
const startingSecurityLevel = location.infiltrationData.startingSecurityLevel;
const difficulty = calculateDifficulty(startingSecurityLevel);
const reward = calculateReward(startingSecurityLevel);
const maxLevel = location.infiltrationData.maxClearanceLevel;
return {
location: {
city: location.city!,
city: locationCity,
name: location.name,
},
reward: {

@ -47,7 +47,7 @@ export function portHandle(n: PortNumber): NetscriptPort {
};
}
export function writePort(n: PortNumber, value: unknown): any {
export function writePort(n: PortNumber, value: unknown): unknown {
const port = getPort(n);
// Primitives don't need to be cloned.
port.add(isObjectLike(value) ? structuredClone(value) : value);
@ -63,15 +63,15 @@ export function tryWritePort(n: PortNumber, value: unknown): boolean {
return true;
}
export function readPort(n: PortNumber): any {
export function readPort(n: PortNumber): unknown {
const port = NetscriptPorts.get(n);
if (!port || !port.data.length) return emptyPortData;
const returnVal = port.data.shift();
const returnVal: unknown = port.data.shift();
if (!port.data.length && !port.resolver) NetscriptPorts.delete(n);
return returnVal;
}
export function peekPort(n: PortNumber): any {
export function peekPort(n: PortNumber): unknown {
const port = NetscriptPorts.get(n);
if (!port || !port.data.length) return emptyPortData;
// Needed to avoid exposing internal objects.

@ -26,6 +26,7 @@ import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork";
import { canAccessBitNodeFeature } from "../../../BitNode/BitNodeUtils";
import { getKeyFromReactElements } from "../../../utils/StringHelperFunctions";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
@ -177,7 +178,7 @@ export function EarningsElement(props: IProps): React.ReactElement {
</TableCell>
</TableRow>
{data.map(([a, b]) => (
<TableRow key={a.toString() + b.toString()}>
<TableRow key={getKeyFromReactElements(a, b)}>
<TableCell classes={{ root: classes.cellNone }}>
<Typography>{a}</Typography>
</TableCell>

@ -231,16 +231,16 @@ function parseOnlyRamCalculate(
prefix: string,
obj: object,
ref: string,
): { func: () => number | number; refDetail: string } | undefined => {
): { func: (() => number) | number; refDetail: string } | undefined => {
if (!obj) {
return;
}
const elem = Object.entries(obj).find(([key]) => key === ref);
if (elem !== undefined && (typeof elem[1] === "function" || typeof elem[1] === "number")) {
return { func: elem[1], refDetail: `${prefix}${ref}` };
return { func: elem[1] as (() => number) | number, refDetail: `${prefix}${ref}` };
}
for (const [key, value] of Object.entries(obj)) {
const found = findFunc(`${key}.`, value, ref);
const found = findFunc(`${key}.`, value as object, ref);
if (found) {
return found;
}

@ -76,7 +76,7 @@ export class Script implements ContentFile {
const ramCalc = calculateRamUsage(this.code, this.filename, this.server, otherScripts);
if (ramCalc.cost && ramCalc.cost >= RamCostConstants.Base) {
this.ramUsage = roundToTwo(ramCalc.cost);
this.ramUsageEntries = ramCalc.entries as RamUsageEntry[];
this.ramUsageEntries = ramCalc.entries;
this.ramCalculationError = null;
return;
}

@ -1,5 +1,4 @@
import React, { useEffect, useRef, useState } from "react";
// @ts-expect-error This library does not have types.
import * as MonacoVim from "monaco-vim";
import type { editor } from "monaco-editor";
type IStandaloneCodeEditor = editor.IStandaloneCodeEditor;
@ -18,8 +17,7 @@ interface IProps {
}
export function useVimEditor({ editor, vim, onOpenNextTab, onOpenPreviousTab, onSave }: IProps) {
// monaco-vim does not have types, so this is an any
const [vimEditor, setVimEditor] = useState<any>(null);
const [vimEditor, setVimEditor] = useState<ReturnType<typeof MonacoVim.initVimMode> | null>(null);
const statusBarRef = useRef<React.ReactElement | null>(null);
const rerender = useRerender();
@ -30,7 +28,6 @@ export function useVimEditor({ editor, vim, onOpenNextTab, onOpenPreviousTab, on
useEffect(() => {
// setup monaco-vim
if (vim && editor && !vimEditor) {
// Using try/catch because MonacoVim does not have types.
try {
setVimEditor(MonacoVim.initVimMode(editor, statusBarRef, StatusBar, rerender));
MonacoVim.VimMode.Vim.defineEx("write", "w", function () {
@ -65,8 +62,7 @@ export function useVimEditor({ editor, vim, onOpenNextTab, onOpenPreviousTab, on
MonacoVim.VimMode.Vim.mapCommand("gT", "action", "prevTabs", {}, { context: "normal" });
editor.focus();
} catch (e) {
console.error("An error occurred while loading monaco-vim:");
console.error(e);
console.error("An error occurred while loading monaco-vim:", e);
}
} else if (!vim) {
// When vim mode is disabled

@ -2,6 +2,7 @@ import { GetServer } from "../../Server/AllServers";
import { editor, Uri } from "monaco-editor";
import { OpenScript } from "./OpenScript";
import { getFileType, FileType } from "../../utils/ScriptTransformer";
import { throwIfReachable } from "../../utils/helpers/throwIfReachable";
function getServerCode(scripts: OpenScript[], index: number): string | null {
const openScript = scripts[index];
@ -48,7 +49,7 @@ function makeModel(hostname: string, filename: string, code: string) {
language = "javascript";
break;
default:
throw new Error(`Invalid file type: ${fileType}. Filename: ${filename}.`);
throwIfReachable(fileType);
}
//if somehow a model already exist return it
return editor.getModel(uri) ?? editor.createModel(code, language, uri);

@ -8,8 +8,9 @@ import Typography from "@mui/material/Typography";
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { SidebarItem, ICreateProps as IItemProps } from "./SidebarItem";
import { SidebarItem, ICreateProps as IItemProps, type SidebarItemProps } from "./SidebarItem";
import type { Page } from "../../ui/Router";
import type { PartialRecord } from "../../Types/Record";
type SidebarAccordionProps = {
key_: string;
@ -19,9 +20,12 @@ type SidebarAccordionProps = {
items: (IItemProps | boolean)[];
icon: React.ReactElement["type"];
sidebarOpen: boolean;
classes: any;
classes: Record<"listitem" | "active", string>;
};
type ClickFnCacheKeyType = (page: Page) => void;
type ClickFnCacheValueType = PartialRecord<Page, SidebarItemProps["clickFn"]>;
// We can't useCallback for this, because in the items map it would be
// called a changing number of times, and hooks can't be called in loops. So
// we set up this explicit cache of function objects instead.
@ -30,7 +34,7 @@ type SidebarAccordionProps = {
// WeakMap prevents memory leaks. We won't drop slices of the cache too soon,
// because the fn keys are themselves memoized elsewhere, which keeps them
// alive and thus keeps the WeakMap entries alive.
const clickFnCache = new WeakMap();
const clickFnCache = new WeakMap<ClickFnCacheKeyType, ClickFnCacheValueType>();
function getClickFn(toWrap: (page: Page) => void, page: Page) {
let first = clickFnCache.get(toWrap);
if (first === undefined) {

@ -18,7 +18,7 @@ export interface ICreateProps {
export interface SidebarItemProps extends ICreateProps {
clickFn: () => void;
flash: boolean;
classes: any;
classes: Record<"listitem" | "active", string>;
sidebarOpen: boolean;
}

@ -17,6 +17,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Settings } from "../Settings/Settings";
import * as React from "react";
import { throwIfReachable } from "../utils/helpers/throwIfReachable";
export interface IProcessOrderRefs {
stockMarket: IStockMarket;
@ -50,8 +51,8 @@ export function processOrders(
return; // Newly created, so no orders to process
}
let stockOrders = orderBook[stock.symbol];
if (stockOrders == null || !(stockOrders.constructor === Array)) {
console.error(`Invalid Order book for ${stock.symbol} in processOrders(): ${stockOrders}`);
if (stockOrders == null || !Array.isArray(stockOrders)) {
console.error(`Invalid Order book for ${stock.symbol} in processOrders(). stockOrders: ${stockOrders}`);
stockOrders = [];
return;
}
@ -88,8 +89,7 @@ export function processOrders(
}
break;
default:
console.warn(`Invalid order type: ${order.type}`);
return;
throwIfReachable(order.type);
}
}
}
@ -137,8 +137,7 @@ function executeOrder(order: Order, refs: IProcessOrderRefs): void {
}
break;
default:
console.warn(`Invalid order type: ${order.type}`);
return;
throwIfReachable(order.type);
}
// Position type, for logging/message purposes

@ -21,6 +21,9 @@ export function check(args: (string | number | boolean)[], server: BaseServer):
Terminal.error(`No script named ${scriptName} is running on the server`);
return;
}
runningScripts.values().next().value.displayLog();
const next = runningScripts.values().next();
if (!next.done) {
next.value.displayLog();
}
}
}

@ -9,11 +9,11 @@ export function expr(args: (string | number | boolean)[]): void {
// Sanitize the math expression
const sanitizedExpr = expr.replace(/[^-()\d/*+.%]/g, "");
let result;
let result: string;
try {
result = eval?.(sanitizedExpr);
result = String(eval?.(sanitizedExpr));
} catch (e) {
Terminal.error(`Could not evaluate expression: ${sanitizedExpr}`);
Terminal.error(`Could not evaluate expression: ${sanitizedExpr}. Error: ${e}.`);
return;
}
Terminal.print(result);

@ -86,7 +86,7 @@ export function TerminalInput(): React.ReactElement {
function getSearchSuggestionPrespace() {
const currentPrefix = `[${Player.getCurrentServer().hostname} /${Terminal.cwd()}]> `;
const prefixLength = `${currentPrefix}${value}`.length;
return Array(prefixLength).fill(" ");
return Array<string>(prefixLength).fill(" ");
}
function modifyInput(mod: Modification): void {
@ -206,7 +206,7 @@ export function TerminalInput(): React.ReactElement {
return () => document.removeEventListener("keydown", keyDown);
});
async function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): Promise<void> {
async function onKeyDown(event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>): Promise<void> {
const ref = terminalInput.current;
// Run command or insert newline
@ -427,7 +427,11 @@ export function TerminalInput(): React.ReactElement {
setPossibilities([]);
resetSearch();
},
onKeyDown: onKeyDown,
onKeyDown: (event) => {
onKeyDown(event).catch((error) => {
console.error(error);
});
},
}}
></TextField>
<Popper

@ -41,7 +41,7 @@ export function TerminalRoot(): React.ReactElement {
const [key, setKey] = useState(0);
useEffect(() => {
const debounced = _.debounce(async () => rerender(), 25, { maxWait: 50 });
const debounced = _.debounce(() => rerender(), 25, { maxWait: 50 });
const unsubscribe = TerminalEvents.subscribe(debounced);
return () => {
debounced.cancel();
@ -51,7 +51,7 @@ export function TerminalRoot(): React.ReactElement {
useEffect(() => {
const clear = () => setKey((key) => key + 1);
const debounced = _.debounce(async () => clear(), 25, { maxWait: 50 });
const debounced = _.debounce(() => clear(), 25, { maxWait: 50 });
const unsubscribe = TerminalClearEvents.subscribe(debounced);
return () => {
debounced.cancel();

@ -917,7 +917,7 @@ export const codingContractTypesMetadata: CodingContractType<any>[] = [
desc: (data: number[][]): string => {
return [
"You are located in the top-left corner of the following grid:\n\n",
`&nbsp;&nbsp;[${data.map((line) => "[" + line + "]").join(",\n&nbsp;&nbsp;&nbsp;")}]\n\n`,
`&nbsp;&nbsp;[${data.map((line) => `[${line}]`).join(",\n&nbsp;&nbsp;&nbsp;")}]\n\n`,
"You are trying to find the shortest path to the bottom-right corner of the grid,",
"but there are obstacles on the grid that you cannot move onto.",
"These obstacles are denoted by '1', while empty spaces are denoted by 0.\n\n",
@ -945,8 +945,8 @@ export const codingContractTypesMetadata: CodingContractType<any>[] = [
const dstX = width - 1;
const minPathLength = dstY + dstX; // Math.abs(dstY - srcY) + Math.abs(dstX - srcX)
const grid: number[][] = new Array(height);
for (let y = 0; y < height; y++) grid[y] = new Array(width).fill(0);
const grid: number[][] = new Array<number[]>(height);
for (let y = 0; y < height; y++) grid[y] = new Array<number>(width).fill(0);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
@ -969,12 +969,12 @@ export const codingContractTypesMetadata: CodingContractType<any>[] = [
const dstY = height - 1;
const dstX = width - 1;
const distance: [number][] = new Array(height);
const distance: number[][] = new Array<number[]>(height);
//const prev: [[number, number] | undefined][] = new Array(height);
const queue: [number, number][] = [];
for (let y = 0; y < height; y++) {
distance[y] = new Array(width).fill(Infinity) as [number];
distance[y] = new Array<number>(width).fill(Infinity);
//prev[y] = new Array(width).fill(undefined) as [undefined];
}
@ -1422,7 +1422,7 @@ export const codingContractTypesMetadata: CodingContractType<any>[] = [
//Attempt to construct one to check if this is correct.
if (sanitizedPlayerAns === "") {
//Verify that there is no solution by attempting to create a proper 2-coloring.
const coloring: (number | undefined)[] = Array(data[0]).fill(undefined);
const coloring: (number | undefined)[] = Array<number | undefined>(data[0]).fill(undefined);
while (coloring.some((val) => val === undefined)) {
//Color a vertex in the graph
const initialVertex: number = coloring.findIndex((val) => val === undefined);
@ -1435,9 +1435,7 @@ export const codingContractTypesMetadata: CodingContractType<any>[] = [
const neighbors: number[] = neighbourhood(v);
//For each vertex u adjacent to v
for (const id in neighbors) {
const u: number = neighbors[id];
for (const u of neighbors) {
//Set the color of u to the opposite of v's color if it is new,
//then add u to the frontier to continue the algorithm.
if (coloring[u] === undefined) {

@ -46,7 +46,7 @@ export const A = (props: React.PropsWithChildren<{ href?: string }>): React.Reac
cursor: "pointer",
}}
>
<CorruptableText content={props.children + ""} spoiler={true} />
<CorruptableText content={String(props.children)} spoiler={true} />
</span>
);
return (

@ -38,7 +38,7 @@ type RowName = SkillRowName | "HP" | "Money";
const OverviewEventEmitter = new EventEmitter();
// These values aren't displayed, they're just used for comparison to check if state has changed
const valUpdaters: Record<RowName, () => any> = {
const valUpdaters: Record<RowName, () => unknown> = {
HP: () => Player.hp.current + "|" + Player.hp.max, // This isn't displayed, it's just compared for updates.
Money: () => Player.money,
Hack: () => Player.skills.hacking,

@ -26,9 +26,11 @@ export function CinematicLine(props: IProps): React.ReactElement {
return;
}
let cancel = false;
(async () => {
await sleep(10).then(() => !cancel && advance());
})();
sleep(10)
.then(() => !cancel && advance())
.catch((error) => {
console.error(error);
});
return () => {
cancel = true;
};

@ -399,7 +399,11 @@ export const ImportSave = (props: { saveData: SaveData; automatic: boolean }): J
<ConfirmationModal
open={isImportModalOpen}
onClose={closeImportModal}
onConfirm={handleImport}
onConfirm={() => {
handleImport().catch((error) => {
console.error(error);
});
}}
confirmationText={
<>
Importing new save game data will <strong>completely wipe</strong> the current game data!

@ -298,7 +298,16 @@ function LogWindow({ hidden, script, onClose }: LogWindowProps): React.ReactElem
return !(bounds.right < 0 || bounds.bottom < 0 || bounds.left > innerWidth || bounds.top > outerWidth);
};
const onDrag = (e: DraggableEvent): void | false => {
/**
* The returned type of onDrag is a bit weird here. The Draggable component expects an onDrag that returns "void | false".
* In that component's internal code, it checks for the explicit "false" value. If onDrag returns false, the component
* cancels the dragging.
*
* That's why they use "void | false" as the returned type. However, in TypeScript, "void" is not supposed to be used
* like that. ESLint will complain "void is not valid as a constituent in a union type". Please check its documentation
* for the reason. In order to solve this problem, I changed the returned type to "undefined | false".
*/
const onDrag = (e: DraggableEvent): undefined | false => {
e.preventDefault();
// bound to body
if (

@ -98,7 +98,7 @@ export function RecoveryRoot({ softReset, errorData, resetError }: IProps): Reac
{sourceError && (
<Box>
<Typography variant="h6" color={Settings.theme.error}>
Error: {sourceError.toString()}
Error: {String(sourceError)}
</Typography>
<br />
</Box>

@ -14,7 +14,7 @@ interface IProps {
const useStyles = makeStyles()({
snackbar: {
// Log popup z-index increments, so let's add a padding to be well above them.
zIndex: `${logBoxBaseZIndex + 1000} !important` as any,
zIndex: `${logBoxBaseZIndex + 1000} !important`,
"& .MuiAlert-icon": {
alignSelf: "center",

@ -1,4 +1,4 @@
// choose random character for generating plaintexts to compress
// choose random characters for generating plaintext to compress
export function comprGenChar(): string {
const r = Math.random();
if (r < 0.4) {
@ -34,13 +34,13 @@ export function comprLZGenerate(): string {
return plain.substring(0, length);
}
// compress plaintest string
// compress plaintext string
export function comprLZEncode(plain: string): string {
// for state[i][j]:
// if i is 0, we're adding a literal of length j
// else, we're adding a backreference of offset i and length j
let cur_state: (string | null)[][] = Array.from(Array(10), () => Array(10).fill(null));
let new_state: (string | null)[][] = Array.from(Array(10), () => Array(10));
let cur_state: (string | null)[][] = Array.from(Array(10), () => Array<string | null>(10).fill(null));
let new_state: (string | null)[][] = Array.from(Array(10), () => Array<string | null>(10));
function set(state: (string | null)[][], i: number, j: number, str: string): void {
const current = state[i][j];

@ -3,7 +3,7 @@ import { Terminal } from "../Terminal";
const deprecatedWarningsGiven = new Set();
export function setDeprecatedProperties(
obj: object,
properties: Record<string, { identifier: string; message: string; value: any }>,
properties: Record<string, { identifier: string; message: string; value: unknown }>,
) {
for (const [name, info] of Object.entries(properties)) {
Object.defineProperty(obj, name, {
@ -11,7 +11,7 @@ export function setDeprecatedProperties(
deprecationWarning(info.identifier, info.message);
return info.value;
},
set: (value: any) => (info.value = value),
set: (value: unknown) => (info.value = value),
enumerable: true,
});
}

@ -5,6 +5,7 @@ import * as allEnums from "../Enums";
import { assertString } from "../Netscript/TypeAssertion";
import { errorMessage } from "../Netscript/ErrorMessages";
import { getRandomIntInclusive } from "./helpers/getRandomIntInclusive";
import { getRecordValues } from "../Types/Record";
interface GetMemberOptions {
/** Whether to use fuzzy matching on the input (case insensitive, ignore spaces and dashes) */
@ -22,7 +23,7 @@ class EnumHelper<EnumObj extends object, EnumMember extends Member<EnumObj> & st
constructor(obj: EnumObj, name: string) {
this.name = name;
this.defaultArgName = name.charAt(0).toLowerCase() + name.slice(1);
this.valueArray = Object.values(obj);
this.valueArray = getRecordValues(obj);
this.valueSet = new Set(this.valueArray);
this.fuzzMap = new Map(this.valueArray.map((val) => [val.toLowerCase().replace(/[ -]+/g, ""), val]));
}

@ -1,10 +1,10 @@
export function HammingEncode(data: number): string {
const enc: number[] = [0];
const data_bits: any[] = data.toString(2).split("").reverse();
data_bits.forEach((e, i, a) => {
a[i] = parseInt(e);
});
const data_bits: number[] = data
.toString(2)
.split("")
.reverse()
.map((value) => parseInt(value));
let k = data_bits.length;
@ -18,35 +18,36 @@ export function HammingEncode(data: number): string {
}
}
let parity: any = 0;
let parityNumber = 0;
/* Figure out the subsection parities */
for (let i = 0; i < enc.length; i++) {
if (enc[i]) {
parity ^= i;
parityNumber ^= i;
}
}
parity = parity.toString(2).split("").reverse();
parity.forEach((e: any, i: any, a: any) => {
a[i] = parseInt(e);
});
const parityArray = parityNumber
.toString(2)
.split("")
.reverse()
.map((value) => parseInt(value));
/* Set the parity bits accordingly */
for (let i = 0; i < parity.length; i++) {
enc[2 ** i] = parity[i] ? 1 : 0;
for (let i = 0; i < parityArray.length; i++) {
enc[2 ** i] = parityArray[i] ? 1 : 0;
}
parity = 0;
parityNumber = 0;
/* Figure out the overall parity for the entire block */
for (let i = 0; i < enc.length; i++) {
if (enc[i]) {
parity++;
parityNumber++;
}
}
/* Finally set the overall parity bit */
enc[0] = parity % 2 == 0 ? 0 : 1;
enc[0] = parityNumber % 2 == 0 ? 0 : 1;
return enc.join("");
}
@ -68,11 +69,11 @@ export function HammingEncodeProperly(data: number): string {
const k: number = 2 ** m - m - 1;
const enc: number[] = [0];
const data_bits: any[] = data.toString(2).split("").reverse();
data_bits.forEach((e, i, a) => {
a[i] = parseInt(e);
});
const data_bits: number[] = data
.toString(2)
.split("")
.reverse()
.map((value) => parseInt(value));
/* Flip endianness as in the original implementation by Hedrauta
* and write the data back to front
@ -83,35 +84,36 @@ export function HammingEncodeProperly(data: number): string {
}
}
let parity: any = 0;
let parityNumber = 0;
/* Figure out the subsection parities */
for (let i = 0; i < n; i++) {
if (enc[i]) {
parity ^= i;
parityNumber ^= i;
}
}
parity = parity.toString(2).split("").reverse();
parity.forEach((e: any, i: any, a: any) => {
a[i] = parseInt(e);
});
const parityArray = parityNumber
.toString(2)
.split("")
.reverse()
.map((value) => parseInt(value));
/* Set the parity bits accordingly */
for (let i = 0; i < m; i++) {
enc[2 ** i] = parity[i] ? 1 : 0;
enc[2 ** i] = parityArray[i] ? 1 : 0;
}
parity = 0;
parityNumber = 0;
/* Figure out the overall parity for the entire block */
for (let i = 0; i < n; i++) {
if (enc[i]) {
parity++;
parityNumber++;
}
}
/* Finally set the overall parity bit */
enc[0] = parity % 2 == 0 ? 0 : 1;
enc[0] = parityNumber % 2 == 0 ? 0 : 1;
return enc.join("");
}
@ -121,8 +123,9 @@ export function HammingDecode(data: string): number {
const bits: number[] = [];
/* TODO why not just work with an array of digits from the start? */
for (const i in data.split("")) {
const bit = parseInt(data[i]);
const bitStringArray = data.split("");
for (let i = 0; i < bitStringArray.length; ++i) {
const bit = parseInt(bitStringArray[i]);
bits[i] = bit;
if (bit) {

@ -125,3 +125,9 @@ export function getNsApiDocumentationUrl(isDevBranch: boolean = CONSTANTS.isDevB
isDevBranch ? "dev" : "stable"
}/markdown/bitburner.ns.md`;
}
export function getKeyFromReactElements(a: string | React.JSX.Element, b: string | React.JSX.Element): string {
const keyOfA = typeof a === "string" ? a : a.key ?? "";
const keyOfb = typeof b === "string" ? b : b.key ?? "";
return keyOfA + keyOfb;
}

@ -0,0 +1,3 @@
export function throwIfReachable(missingCase: never) {
throw new Error(`The case of ${missingCase} was not handled.`);
}