Skip to content

Commit ea90e0a

Browse files
authored
refactor: dont allow nullable numbers (@Miodec) (monkeytypegame#6564)
Enables strict boolean expressions rule for nullable numbers
1 parent cde852c commit ea90e0a

File tree

17 files changed

+156
-42
lines changed

17 files changed

+156
-42
lines changed

backend/src/api/controllers/result.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,12 @@ import {
5252
XpBreakdown,
5353
} from "@monkeytype/contracts/schemas/results";
5454
import { Mode } from "@monkeytype/contracts/schemas/shared";
55-
import { mapRange, roundTo2, stdDev } from "@monkeytype/util/numbers";
55+
import {
56+
isSafeNumber,
57+
mapRange,
58+
roundTo2,
59+
stdDev,
60+
} from "@monkeytype/util/numbers";
5661
import {
5762
getCurrentDayTimestamp,
5863
getStartOfDayTimestamp,
@@ -321,7 +326,10 @@ export async function addResult(
321326
const earliestPossible =
322327
(lastResult?.timestamp ?? 0) + testDurationMilis + incompleteTestsMilis;
323328
const nowNoMilis = Math.floor(Date.now() / 1000) * 1000;
324-
if (lastResult?.timestamp && nowNoMilis < earliestPossible - 1000) {
329+
if (
330+
isSafeNumber(lastResult?.timestamp) &&
331+
nowNoMilis < earliestPossible - 1000
332+
) {
325333
void addLog(
326334
"invalid_result_spacing",
327335
{
@@ -777,7 +785,7 @@ async function calculateXp(
777785
Logger.error(`Could not fetch last result: ${getLastResultError}`);
778786
}
779787

780-
if (lastResult?.timestamp) {
788+
if (isSafeNumber(lastResult?.timestamp)) {
781789
const lastResultDay = getStartOfDayTimestamp(lastResult.timestamp);
782790
const today = getCurrentDayTimestamp();
783791
if (lastResultDay !== today) {

backend/src/workers/later-worker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import LaterQueue, {
1616
import { recordTimeToCompleteJob } from "../utils/prometheus";
1717
import { WeeklyXpLeaderboard } from "../services/weekly-xp-leaderboard";
1818
import { MonkeyMail } from "@monkeytype/contracts/schemas/users";
19-
import { mapRange } from "@monkeytype/util/numbers";
19+
import { isSafeNumber, mapRange } from "@monkeytype/util/numbers";
2020

2121
async function handleDailyLeaderboardResults(
2222
ctx: LaterTaskContexts["daily-leaderboard-results"]
@@ -74,7 +74,7 @@ async function handleDailyLeaderboardResults(
7474
)
7575
.max();
7676

77-
if (!xpReward) return;
77+
if (!isSafeNumber(xpReward)) return;
7878

7979
const rewardMail = buildMonkeyMail({
8080
subject: "Daily leaderboard placement",
@@ -164,7 +164,7 @@ async function handleWeeklyXpLeaderboardResults(
164164
)
165165
.max();
166166

167-
if (!xpReward) return;
167+
if (!isSafeNumber(xpReward)) return;
168168

169169
const rewardMail = buildMonkeyMail({
170170
subject: "Weekly XP Leaderboard placement",

frontend/src/ts/elements/monkey-power.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as ThemeColors from "./theme-colors";
22
import * as SlowTimer from "../states/slow-timer";
33
import Config from "../config";
4+
import { isSafeNumber } from "@monkeytype/util/numbers";
45

56
type Particle = {
67
x: number;
@@ -85,7 +86,7 @@ function createParticle(x: number, y: number, color: string): Particle {
8586
* @param {Particle} particle
8687
*/
8788
function updateParticle(particle: Particle): void {
88-
if (!ctx.canvas || !ctx.deltaTime) return;
89+
if (!ctx.canvas || !isSafeNumber(ctx.deltaTime)) return;
8990

9091
particle.prev.x = particle.x;
9192
particle.prev.y = particle.y;
@@ -123,7 +124,7 @@ export function init(): void {
123124
}
124125

125126
function render(): void {
126-
if (!ctx.lastFrame || !ctx.context2d || !ctx.canvas) return;
127+
if (!isSafeNumber(ctx.lastFrame) || !ctx.context2d || !ctx.canvas) return;
127128
ctx.rendering = true;
128129
const time = Date.now();
129130
ctx.deltaTime = (time - ctx.lastFrame) / 1000;
@@ -163,7 +164,7 @@ function render(): void {
163164
}
164165

165166
export function reset(immediate = false): void {
166-
if (!ctx.resetTimeOut) return;
167+
if (!isSafeNumber(ctx.resetTimeOut)) return;
167168
delete ctx.resetTimeOut;
168169

169170
clearTimeout(ctx.resetTimeOut);
@@ -213,7 +214,7 @@ export async function addPower(good = true, extra = false): Promise<void> {
213214
"transform",
214215
`translate(${shake[0]}px, ${shake[1]}px)`
215216
);
216-
if (ctx.resetTimeOut) clearTimeout(ctx.resetTimeOut);
217+
if (isSafeNumber(ctx.resetTimeOut)) clearTimeout(ctx.resetTimeOut);
217218
ctx.resetTimeOut = setTimeout(reset, 2000) as unknown as number;
218219
}
219220

frontend/src/ts/elements/profile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export async function update(
172172
console.debug("isToday", isToday);
173173
console.debug("isYesterday", isYesterday);
174174

175-
const offsetString = streakOffset
175+
const offsetString = Numbers.isSafeNumber(streakOffset)
176176
? `(${streakOffset > 0 ? "+" : ""}${streakOffset} offset)`
177177
: "";
178178

frontend/src/ts/elements/psa.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { z } from "zod";
99
import { LocalStorageWithSchema } from "../utils/local-storage-with-schema";
1010
import { IdSchema } from "@monkeytype/contracts/schemas/util";
1111
import { tryCatch } from "@monkeytype/util/trycatch";
12+
import { isSafeNumber } from "@monkeytype/util/numbers";
1213

1314
const confirmedPSAs = new LocalStorageWithSchema({
1415
key: "confirmedPSAs",
@@ -136,7 +137,7 @@ export async function show(): Promise<void> {
136137
}
137138
const localmemory = getMemory();
138139
latest.forEach((psa) => {
139-
if (psa.date) {
140+
if (isSafeNumber(psa.date)) {
140141
const dateObj = new Date(psa.date);
141142
const diff = psa.date - Date.now();
142143
const string = secondsToString(

frontend/src/ts/elements/test-activity-calendar.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { UTCDateMini } from "@date-fns/utc/date/mini";
2+
import { safeNumber } from "@monkeytype/util/numbers";
23
import {
34
format,
45
endOfMonth,
@@ -219,7 +220,7 @@ export class ModifiableTestActivityCalendar
219220
const lastDay = new UTCDateMini(this.lastDay);
220221
if (isSameDay(date, lastDay)) {
221222
const last = this.data.length - 1;
222-
this.data[last] = (this.data[last] || 0) + 1;
223+
this.data[last] = (safeNumber(this.data[last]) ?? 0) + 1;
223224
} else if (isBefore(date, lastDay)) {
224225
throw new Error("cannot alter data in the past.");
225226
} else {

frontend/src/ts/elements/test-activity.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
TestActivityCalendar,
88
TestActivityMonth,
99
} from "./test-activity-calendar";
10+
import { safeNumber } from "@monkeytype/util/numbers";
1011

1112
let yearSelector: SlimSelect | undefined = undefined;
1213

@@ -21,7 +22,10 @@ export function init(
2122
$("#testActivity").removeClass("hidden");
2223

2324
yearSelector = getYearSelector();
24-
initYearSelector("current", userSignUpDate?.getFullYear() || 2022);
25+
initYearSelector(
26+
"current",
27+
safeNumber(userSignUpDate?.getFullYear()) ?? 2022
28+
);
2529
updateLabels(calendar.firstDayOfWeek);
2630
update(calendar);
2731
}

frontend/src/ts/elements/xp-bar.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as Levels from "../utils/levels";
33
import { getAll } from "./theme-colors";
44
import * as SlowTimer from "../states/slow-timer";
55
import { XpBreakdown } from "@monkeytype/contracts/schemas/results";
6-
import { mapRange } from "@monkeytype/util/numbers";
6+
import { isSafeNumber, mapRange } from "@monkeytype/util/numbers";
77

88
let breakdownVisible = false;
99
let skip = false;
@@ -268,12 +268,12 @@ async function animateXpBreakdown(
268268

269269
await Misc.sleep(delay);
270270

271-
if (breakdown.fullAccuracy) {
271+
if (isSafeNumber(breakdown.fullAccuracy)) {
272272
await Misc.sleep(delay);
273273
total += breakdown.fullAccuracy;
274274
void flashTotalXp(total);
275275
await addBreakdownListItem("perfect", breakdown.fullAccuracy);
276-
} else if (breakdown.corrected) {
276+
} else if (isSafeNumber(breakdown.corrected)) {
277277
await Misc.sleep(delay);
278278
total += breakdown.corrected;
279279
void flashTotalXp(total);
@@ -282,19 +282,19 @@ async function animateXpBreakdown(
282282

283283
if (skip) return;
284284

285-
if (breakdown.quote) {
285+
if (isSafeNumber(breakdown.quote)) {
286286
await Misc.sleep(delay);
287287
total += breakdown.quote;
288288
void flashTotalXp(total);
289289
await addBreakdownListItem("quote", breakdown.quote);
290290
} else {
291-
if (breakdown.punctuation) {
291+
if (isSafeNumber(breakdown.punctuation)) {
292292
await Misc.sleep(delay);
293293
total += breakdown.punctuation;
294294
void flashTotalXp(total);
295295
await addBreakdownListItem("punctuation", breakdown.punctuation);
296296
}
297-
if (breakdown.numbers) {
297+
if (isSafeNumber(breakdown.numbers)) {
298298
await Misc.sleep(delay);
299299
total += breakdown.numbers;
300300
void flashTotalXp(total);
@@ -304,7 +304,7 @@ async function animateXpBreakdown(
304304

305305
if (skip) return;
306306

307-
if (breakdown.funbox) {
307+
if (isSafeNumber(breakdown.funbox)) {
308308
await Misc.sleep(delay);
309309
total += breakdown.funbox;
310310
void flashTotalXp(total);
@@ -313,7 +313,7 @@ async function animateXpBreakdown(
313313

314314
if (skip) return;
315315

316-
if (breakdown.streak) {
316+
if (isSafeNumber(breakdown.streak)) {
317317
await Misc.sleep(delay);
318318
total += breakdown.streak;
319319
void flashTotalXp(total);
@@ -322,7 +322,7 @@ async function animateXpBreakdown(
322322

323323
if (skip) return;
324324

325-
if (breakdown.accPenalty) {
325+
if (isSafeNumber(breakdown.accPenalty)) {
326326
await Misc.sleep(delay);
327327
total -= breakdown.accPenalty;
328328
void flashTotalXp(total);
@@ -331,7 +331,7 @@ async function animateXpBreakdown(
331331

332332
if (skip) return;
333333

334-
if (breakdown.incomplete) {
334+
if (isSafeNumber(breakdown.incomplete)) {
335335
await Misc.sleep(delay);
336336
total += breakdown.incomplete;
337337
void flashTotalXp(total);
@@ -340,7 +340,7 @@ async function animateXpBreakdown(
340340

341341
if (skip) return;
342342

343-
if (breakdown.configMultiplier) {
343+
if (isSafeNumber(breakdown.configMultiplier)) {
344344
await Misc.sleep(delay);
345345
total *= breakdown.configMultiplier;
346346
void flashTotalXp(total);
@@ -352,7 +352,7 @@ async function animateXpBreakdown(
352352

353353
if (skip) return;
354354

355-
if (breakdown.daily) {
355+
if (isSafeNumber(breakdown.daily)) {
356356
await Misc.sleep(delay);
357357
total += breakdown.daily;
358358
void flashTotalXp(total);

frontend/src/ts/modals/quote-rate.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as DB from "../db";
55
import * as Loader from "../elements/loader";
66
import * as Notifications from "../elements/notifications";
77
import AnimatedModal, { ShowOptions } from "../utils/animated-modal";
8+
import { isSafeNumber } from "@monkeytype/util/numbers";
89

910
let rating = 0;
1011

@@ -33,11 +34,16 @@ function reset(): void {
3334
}
3435

3536
function getRatingAverage(quoteStats: QuoteStats): number {
36-
if (!quoteStats.totalRating || !quoteStats.ratings) {
37-
return 0;
37+
if (
38+
isSafeNumber(quoteStats.ratings) &&
39+
isSafeNumber(quoteStats.totalRating) &&
40+
quoteStats.ratings > 0 &&
41+
quoteStats.totalRating > 0
42+
) {
43+
return Math.round((quoteStats.totalRating / quoteStats.ratings) * 10) / 10;
3844
}
3945

40-
return Math.round((quoteStats.totalRating / quoteStats.ratings) * 10) / 10;
46+
return 0;
4147
}
4248

4349
export async function getQuoteStats(
@@ -66,7 +72,7 @@ export async function getQuoteStats(
6672
}
6773

6874
quoteStats = response.body.data as QuoteStats;
69-
if (quoteStats !== undefined && !quoteStats.average) {
75+
if (quoteStats !== undefined && quoteStats.average === undefined) {
7076
quoteStats.average = getRatingAverage(quoteStats);
7177
}
7278

@@ -118,7 +124,7 @@ export function show(quote: Quote, showOptions?: ShowOptions): void {
118124
const snapshot = DB.getSnapshot();
119125
const alreadyRated =
120126
snapshot?.quoteRatings?.[currentQuote.language]?.[currentQuote.id];
121-
if (alreadyRated) {
127+
if (isSafeNumber(alreadyRated)) {
122128
rating = alreadyRated;
123129
}
124130
refreshStars();
@@ -163,7 +169,7 @@ async function submit(): Promise<void> {
163169

164170
const languageRatings = quoteRatings?.[currentQuote.language] ?? {};
165171

166-
if (languageRatings?.[currentQuote.id]) {
172+
if (isSafeNumber(languageRatings?.[currentQuote.id])) {
167173
const oldRating = quoteRatings[currentQuote.language]?.[
168174
currentQuote.id
169175
] as number;
@@ -180,7 +186,10 @@ async function submit(): Promise<void> {
180186
Notifications.add("Rating updated", 1);
181187
} else {
182188
languageRatings[currentQuote.id] = rating;
183-
if (quoteStats?.ratings && quoteStats.totalRating) {
189+
if (
190+
isSafeNumber(quoteStats?.ratings) &&
191+
isSafeNumber(quoteStats.totalRating)
192+
) {
184193
quoteStats.ratings++;
185194
quoteStats.totalRating += rating;
186195
} else {

frontend/src/ts/pages/account.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ let visibleTableLines = 0;
5555
function loadMoreLines(lineIndex?: number): void {
5656
if (filteredResults === undefined || filteredResults.length === 0) return;
5757
let newVisibleLines;
58-
if (lineIndex && lineIndex > visibleTableLines) {
58+
if (Numbers.isSafeNumber(lineIndex) && lineIndex > visibleTableLines) {
5959
newVisibleLines = Math.ceil(lineIndex / 10) * 10;
6060
} else {
6161
newVisibleLines = visibleTableLines + 10;

0 commit comments

Comments
 (0)