Fix all strict errors in /client (#1489)

## Description:

Second PR to fix issues in order to enable strict mode.

## Specifics

1. Most important change: Turned off errors for Class variables not
initialized in constructor. I've noticed that pretty much all Classes in
the project have at least one occurence of that issue. And fixing it
properly would require a large refactor across the whole project. So
disabling the rule seems like a good solution in this case.

#1075 

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA aggreement (only required once).

## Please put your Discord username so you can be contacted if a bug or
regression is found:

azlod
This commit is contained in:
Antoine
2025-07-29 22:24:52 +02:00
committed by GitHub
parent 0726449b6e
commit dc1f79d090
15 changed files with 69 additions and 55 deletions
+5 -3
View File
@@ -154,7 +154,9 @@ export class HostLobbyModal extends LitElement {
<div class="flex flex-row flex-wrap justify-center gap-4">
${maps.map((mapValue) => {
const mapKey = Object.keys(GameMapType).find(
(key) => GameMapType[key] === mapValue,
(key) =>
GameMapType[key as keyof typeof GameMapType] ===
mapValue,
);
return html`
<div
@@ -214,7 +216,7 @@ export class HostLobbyModal extends LitElement {
></difficulty-display>
<p class="option-card-title">
${translateText(
`difficulty.${DifficultyDescription[key]}`,
`difficulty.${DifficultyDescription[key as keyof typeof DifficultyDescription]}`,
)}
</p>
</div>
@@ -589,7 +591,7 @@ export class HostLobbyModal extends LitElement {
await this.putGameConfig();
console.log(
`Starting private game with map: ${GameMapType[this.selectedMap]} ${this.useRandomMap ? " (Randomly selected)" : ""}`,
`Starting private game with map: ${GameMapType[this.selectedMap as keyof typeof GameMapType]} ${this.useRandomMap ? " (Randomly selected)" : ""}`,
);
this.close();
const config = await getServerConfigFromClient();
+12 -3
View File
@@ -58,6 +58,11 @@ declare global {
spaNewPage: (url: string) => void;
};
}
// Extend the global interfaces to include your custom events
interface DocumentEventMap {
"join-lobby": CustomEvent<JoinLobbyEvent>;
}
}
export interface JoinLobbyEvent {
@@ -383,14 +388,18 @@ class Client {
window.addEventListener("popstate", onHashUpdate);
window.addEventListener("hashchange", onHashUpdate);
function updateSliderProgress(slider) {
function updateSliderProgress(slider: HTMLInputElement) {
const percent =
((slider.value - slider.min) / (slider.max - slider.min)) * 100;
((Number(slider.value) - Number(slider.min)) /
(Number(slider.max) - Number(slider.min))) *
100;
slider.style.setProperty("--progress", `${percent}%`);
}
document
.querySelectorAll("#bots-count, #private-lobby-bots-count")
.querySelectorAll<HTMLInputElement>(
"#bots-count, #private-lobby-bots-count",
)
.forEach((slider) => {
updateSliderProgress(slider);
slider.addEventListener("input", () => updateSliderProgress(slider));
+5 -3
View File
@@ -67,7 +67,9 @@ export class SinglePlayerModal extends LitElement {
<div class="flex flex-row flex-wrap justify-center gap-4">
${maps.map((mapValue) => {
const mapKey = Object.keys(GameMapType).find(
(key) => GameMapType[key] === mapValue,
(key) =>
GameMapType[key as keyof typeof GameMapType] ===
mapValue,
);
return html`
<div
@@ -129,7 +131,7 @@ export class SinglePlayerModal extends LitElement {
></difficulty-display>
<p class="option-card-title">
${translateText(
`difficulty.${DifficultyDescription[key]}`,
`difficulty.${DifficultyDescription[key as keyof typeof DifficultyDescription]}`,
)}
</p>
</div>
@@ -389,7 +391,7 @@ export class SinglePlayerModal extends LitElement {
}
console.log(
`Starting single player game with map: ${GameMapType[this.selectedMap]}${this.useRandomMap ? " (Randomly selected)" : ""}`,
`Starting single player game with map: ${GameMapType[this.selectedMap as keyof typeof GameMapType]}${this.useRandomMap ? " (Randomly selected)" : ""}`,
);
const clientID = generateID();
const gameID = generateID();
+1 -1
View File
@@ -404,7 +404,7 @@ export class BuildMenu extends LitElement implements Layer {
return html`
<div
class="build-menu ${this._hidden ? "hidden" : ""}"
@contextmenu=${(e) => e.preventDefault()}
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
${this.filteredBuildTable.map(
(row) => html`
@@ -26,10 +26,6 @@ export class ChatDisplay extends LitElement implements Layer {
private active: boolean = false;
private updateMap = new Map([
[GameUpdateType.DisplayEvent, (u) => this.onDisplayMessageEvent(u)],
]);
@state() private _hidden: boolean = false;
@state() private newEvents: number = 0;
@state() private chatEvents: ChatEvent[] = [];
+1 -1
View File
@@ -214,7 +214,7 @@ export class ControlPanel extends LitElement implements Layer {
class="${this._isVisible
? "w-full sm:max-w-[320px] text-sm sm:text-base bg-gray-800/70 p-2 pr-3 sm:p-4 shadow-lg sm:rounded-lg backdrop-blur"
: "hidden"}"
@contextmenu=${(e) => e.preventDefault()}
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
<div class="block bg-black/30 text-white mb-4 p-2 rounded">
<div class="flex justify-between mb-1">
+2 -2
View File
@@ -62,8 +62,8 @@ export class EmojiTable extends LitElement {
<div
class="bg-slate-800 max-w-[95vw] max-h-[95vh] pt-[15px] pb-[15px] fixed flex flex-col -translate-x-1/2 -translate-y-1/2
items-center rounded-[10px] z-[9999] top-[50%] left-[50%] justify-center"
@contextmenu=${(e) => e.preventDefault()}
@wheel=${(e) => e.stopPropagation()}
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
@wheel=${(e: WheelEvent) => e.stopPropagation()}
>
<!-- Close button -->
<button
+12 -12
View File
@@ -142,20 +142,20 @@ export class EventsDisplay extends LitElement implements Layer {
this.requestUpdate();
}
private updateMap = new Map([
[GameUpdateType.DisplayEvent, (u) => this.onDisplayMessageEvent(u)],
[GameUpdateType.DisplayChatEvent, (u) => this.onDisplayChatEvent(u)],
[GameUpdateType.AllianceRequest, (u) => this.onAllianceRequestEvent(u)],
private updateMap = [
[GameUpdateType.DisplayEvent, this.onDisplayMessageEvent.bind(this)],
[GameUpdateType.DisplayChatEvent, this.onDisplayChatEvent.bind(this)],
[GameUpdateType.AllianceRequest, this.onAllianceRequestEvent.bind(this)],
[
GameUpdateType.AllianceRequestReply,
(u) => this.onAllianceRequestReplyEvent(u),
this.onAllianceRequestReplyEvent.bind(this),
],
[GameUpdateType.BrokeAlliance, (u) => this.onBrokeAllianceEvent(u)],
[GameUpdateType.TargetPlayer, (u) => this.onTargetPlayerEvent(u)],
[GameUpdateType.Emoji, (u) => this.onEmojiMessageEvent(u)],
[GameUpdateType.UnitIncoming, (u) => this.onUnitIncomingEvent(u)],
[GameUpdateType.AllianceExpired, (u) => this.onAllianceExpiredEvent(u)],
]);
[GameUpdateType.BrokeAlliance, this.onBrokeAllianceEvent.bind(this)],
[GameUpdateType.TargetPlayer, this.onTargetPlayerEvent.bind(this)],
[GameUpdateType.Emoji, this.onEmojiMessageEvent.bind(this)],
[GameUpdateType.UnitIncoming, this.onUnitIncomingEvent.bind(this)],
[GameUpdateType.AllianceExpired, this.onAllianceExpiredEvent.bind(this)],
] as const;
constructor() {
super();
@@ -189,7 +189,7 @@ export class EventsDisplay extends LitElement implements Layer {
const updates = this.game.updatesSinceLastTick();
if (updates) {
for (const [ut, fn] of this.updateMap) {
updates[ut]?.forEach(fn);
updates[ut]?.forEach(fn as (event: unknown) => void);
}
}
+1 -1
View File
@@ -38,7 +38,7 @@ export class HeadsUpMessage extends LitElement implements Layer {
w-full justify-evenly h-8 lg:h-10 md:top-[70px] left-0 lg:left-4
bg-opacity-60 bg-gray-900 rounded-md lg:rounded-lg
backdrop-blur-md text-white text-md lg:text-xl p-1 lg:p-2"
@contextmenu=${(e) => e.preventDefault()}
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
${translateText("heads_up_message.choose_spawn")}
</div>
+5 -5
View File
@@ -1,7 +1,7 @@
export interface Layer {
init?();
tick?();
renderLayer?(context: CanvasRenderingContext2D);
shouldTransform?(): boolean;
redraw?(): void;
init?: () => void;
tick?: () => void;
renderLayer?: (context: CanvasRenderingContext2D) => void;
shouldTransform?: () => boolean;
redraw?: () => void;
}
+2 -2
View File
@@ -14,7 +14,7 @@ const button = ({
classes = "",
onClick = () => {},
title = "",
children,
children = "",
}) => html`
<button
class="flex items-center justify-center p-1
@@ -167,7 +167,7 @@ export class OptionsMenu extends LitElement implements Layer {
return html`
<div
class="top-0 lg:top-4 right-0 lg:right-4 z-50 pointer-events-auto"
@contextmenu=${(e) => e.preventDefault()}
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
<div
class="bg-opacity-60 bg-gray-900 p-1 lg:p-2 rounded-es-sm lg:rounded-lg backdrop-blur-md"
@@ -340,7 +340,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
return html`
<div
class="block lg:flex fixed top-[150px] right-0 w-full z-50 flex-col max-w-[180px]"
@contextmenu=${(e) => e.preventDefault()}
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
<div
class="bg-gray-800/70 backdrop-blur-sm shadow-xs rounded-lg shadow-lg transition-all duration-300 text-white text-lg md:text-base ${containerClasses}"
+17 -13
View File
@@ -162,10 +162,10 @@ export class PlayerPanel extends LitElement implements Layer {
return this;
}
private ctModal;
private ctModal: ChatModal;
init() {
this.eventBus.on(MouseUpEvent, (e: MouseEvent) => this.hide());
this.eventBus.on(MouseUpEvent, () => this.hide());
this.ctModal = document.querySelector("chat-modal") as ChatModal;
}
@@ -234,8 +234,8 @@ export class PlayerPanel extends LitElement implements Layer {
return html`
<div
class="fixed inset-0 flex items-center justify-center z-[1001] pointer-events-none overflow-auto"
@contextmenu=${(e) => e.preventDefault()}
@wheel=${(e) => e.stopPropagation()}
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
@wheel=${(e: MouseEvent) => e.stopPropagation()}
>
<div
class="pointer-events-auto max-h-[90vh] overflow-y-auto min-w-[240px] w-auto px-4 py-2"
@@ -364,7 +364,8 @@ export class PlayerPanel extends LitElement implements Layer {
<!-- Action buttons -->
<div class="flex justify-center gap-2">
<button
@click=${(e) => this.handleChat(e, myPlayer, other)}
@click=${(e: MouseEvent) =>
this.handleChat(e, myPlayer, other)}
class="w-10 h-10 flex items-center justify-center
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
text-white rounded-lg transition-colors"
@@ -373,7 +374,8 @@ export class PlayerPanel extends LitElement implements Layer {
</button>
${canTarget
? html`<button
@click=${(e) => this.handleTargetClick(e, other)}
@click=${(e: MouseEvent) =>
this.handleTargetClick(e, other)}
class="w-10 h-10 flex items-center justify-center
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
text-white rounded-lg transition-colors"
@@ -383,7 +385,7 @@ export class PlayerPanel extends LitElement implements Layer {
: ""}
${canBreakAlliance
? html`<button
@click=${(e) =>
@click=${(e: MouseEvent) =>
this.handleBreakAllianceClick(e, myPlayer, other)}
class="w-10 h-10 flex items-center justify-center
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
@@ -398,7 +400,7 @@ export class PlayerPanel extends LitElement implements Layer {
: ""}
${canSendAllianceRequest
? html`<button
@click=${(e) =>
@click=${(e: MouseEvent) =>
this.handleAllianceClick(e, myPlayer, other)}
class="w-10 h-10 flex items-center justify-center
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
@@ -409,7 +411,7 @@ export class PlayerPanel extends LitElement implements Layer {
: ""}
${canDonate
? html`<button
@click=${(e) =>
@click=${(e: MouseEvent) =>
this.handleDonateTroopClick(e, myPlayer, other)}
class="w-10 h-10 flex items-center justify-center
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
@@ -424,7 +426,7 @@ export class PlayerPanel extends LitElement implements Layer {
: ""}
${canDonate
? html`<button
@click=${(e) =>
@click=${(e: MouseEvent) =>
this.handleDonateGoldClick(e, myPlayer, other)}
class="w-10 h-10 flex items-center justify-center
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
@@ -435,7 +437,8 @@ export class PlayerPanel extends LitElement implements Layer {
: ""}
${canSendEmoji
? html`<button
@click=${(e) => this.handleEmojiClick(e, myPlayer, other)}
@click=${(e: MouseEvent) =>
this.handleEmojiClick(e, myPlayer, other)}
class="w-10 h-10 flex items-center justify-center
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
text-white rounded-lg transition-colors"
@@ -446,7 +449,8 @@ export class PlayerPanel extends LitElement implements Layer {
</div>
${canEmbargo && other !== myPlayer
? html`<button
@click=${(e) => this.handleEmbargoClick(e, myPlayer, other)}
@click=${(e: MouseEvent) =>
this.handleEmbargoClick(e, myPlayer, other)}
class="w-100 h-10 flex items-center justify-center
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
text-white rounded-lg transition-colors"
@@ -456,7 +460,7 @@ export class PlayerPanel extends LitElement implements Layer {
: ""}
${!canEmbargo && other !== myPlayer
? html`<button
@click=${(e) =>
@click=${(e: MouseEvent) =>
this.handleStopEmbargoClick(e, myPlayer, other)}
class="w-100 h-10 flex items-center justify-center
bg-opacity-50 bg-gray-700 hover:bg-opacity-70
+3 -3
View File
@@ -1,7 +1,7 @@
import { LitElement, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { EventBus } from "../../../core/EventBus";
import { GameMode } from "../../../core/game/Game";
import { GameMode, Team } from "../../../core/game/Game";
import { GameView, PlayerView } from "../../../core/game/GameView";
import { renderNumber, translateText } from "../../Utils";
import { Layer } from "./Layer";
@@ -47,7 +47,7 @@ export class TeamStats extends LitElement implements Layer {
private updateTeamStats() {
const players = this.game.playerViews();
const grouped: Record<number, PlayerView[]> = {};
const grouped: Record<Team, PlayerView[]> = {};
for (const player of players) {
const team = player.team();
@@ -102,7 +102,7 @@ export class TeamStats extends LitElement implements Layer {
.visible
? ""
: "hidden"}"
@contextmenu=${(e) => e.preventDefault()}
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
<div
class="grid w-full"
+2 -1
View File
@@ -20,7 +20,8 @@
"experimentalDecorators": true,
"resolveJsonModule": true,
"strictNullChecks": true,
"useDefineForClassFields": false
"useDefineForClassFields": false,
"strictPropertyInitialization": false
},
"include": [
"src/**/*",