TypeCheck Frontend (#2394)

We recently had an error that would've been caught by type checking in the frontend (see #2393). This PR implements type checking so that future errors will be caught.

Additionally, this PR contains miscellaneous frontend cleanup that's tangentially related to type-checking.
This commit is contained in:
Sam Freund
2026-05-05 10:24:19 -05:00
committed by GitHub
parent d587cd19bb
commit 2372e110f9
43 changed files with 578 additions and 388 deletions

View File

@@ -40,18 +40,13 @@ export class AutoReconnectingWebsocket {
* Send data over the websocket. This is a no-op if the websocket is not in the OPEN state.
*
* @param data data to send
* @param encodeData whether or not to encode the data using msgpack (defaults to true)
* @see isConnected
*
*/
send(data, encodeData = true) {
send(data: unknown) {
// Only send data if the websocket is open
if (this.isConnected()) {
if (encodeData) {
this.websocket?.send(encode(data));
} else {
this.websocket?.send(data);
}
this.websocket?.send(encode(data));
}
}

View File

@@ -1,6 +1,6 @@
import { useStateStore } from "@/stores/StateStore";
import type { Resolution } from "@/types/SettingTypes";
import axios from "axios";
import type { PVCameraInfo, Resolution } from "@/types/SettingTypes";
import axios, { type AxiosRequestConfig } from "axios";
export const resolutionsAreEqual = (a: Resolution, b: Resolution) => {
return a.height === b.height && a.width === b.width;
@@ -51,15 +51,16 @@ export const forceReloadPage = async () => {
export const getResolutionString = (resolution: Resolution): string => `${resolution.width}x${resolution.height}`;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const parseJsonFile = async <T extends Record<string, any>>(file: File): Promise<T> => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = (event) => {
const target: FileReader | null = event.target;
if (target === null) reject();
if (target === null) reject(new Error("FileReader event target is null"));
else resolve(JSON.parse(target.result as string) as T);
};
fileReader.onerror = (error) => reject(error);
fileReader.onerror = () => reject(new Error("Error reading file"));
fileReader.readAsText(file);
});
};
@@ -73,7 +74,13 @@ export const parseJsonFile = async <T extends Record<string, any>>(file: File):
* @param config Optional axios request configuration
* @returns A promise that resolves to true if the POST request is successful, or false if an error occurs.
*/
export const axiosPost = async (url: string, description: string, data?: any, config?: any): Promise<boolean> => {
export const axiosPost = async (
url: string,
description: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data?: any,
config?: AxiosRequestConfig
): Promise<boolean> => {
try {
await axios.post(url, data, config);
useStateStore().showSnackbarMessage({
@@ -81,6 +88,7 @@ export const axiosPost = async (url: string, description: string, data?: any, co
color: "success"
});
return true;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
if (error.response) {
useStateStore().showSnackbarMessage({
@@ -101,3 +109,23 @@ export const axiosPost = async (url: string, description: string, data?: any, co
return false;
}
};
type CameraInfoDetails = Partial<
NonNullable<PVCameraInfo["PVUsbCameraInfo"]> &
NonNullable<PVCameraInfo["PVCSICameraInfo"]> &
NonNullable<PVCameraInfo["PVFileCameraInfo"]>
>;
export const cameraInfoFor = (camera: PVCameraInfo | null): CameraInfoDetails => {
if (!camera) return {};
if (camera.PVUsbCameraInfo) {
return camera.PVUsbCameraInfo;
}
if (camera.PVCSICameraInfo) {
return camera.PVCSICameraInfo;
}
if (camera.PVFileCameraInfo) {
return camera.PVFileCameraInfo;
}
return {};
};