refactor(frontend): drop file-level eslint-disable from utils/index

- ObjectUtil.clone/deepClone become generic <T>
- cloneProps/delProps accept `object` (cast internally to AnyRecord)
- equals accepts `unknown` with proper narrowing
- ColorUtils.usageColor narrows data/threshold to `number`; total widened
  to `number | { valueOf(): number } | null | undefined` so Dayjs works
- Utils.debounce replaces `const self = this` with lexical arrow
  closure (no-this-alias clean)
- InboundList._expiryTime narrowed from `unknown` to `{ valueOf(): number } | null`
- Single-line eslint-disable remains on `Msg<T = any>` and HttpUtil
  generic defaults (idiomatic API envelope; changing default to unknown
  cascades through 34 consumer files)
This commit is contained in:
MHSanaei 2026-05-25 02:06:19 +02:00
parent 3ca776f9c9
commit 9ee9b8b39f
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
2 changed files with 42 additions and 28 deletions

View file

@ -57,7 +57,7 @@ interface DBInboundRecord extends ProtocolFlags {
down: number; down: number;
total: number; total: number;
expiryTime: number; expiryTime: number;
_expiryTime: unknown; _expiryTime: { valueOf(): number } | null;
nodeId?: number | null; nodeId?: number | null;
toInbound: () => { toInbound: () => {
stream?: { network?: string; isTls?: boolean; isReality?: boolean }; stream?: { network?: string; isTls?: boolean; isReality?: boolean };

View file

@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-this-alias */
import axios from 'axios'; import axios from 'axios';
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
import { getMessage } from './messageBus'; import { getMessage } from './messageBus';
type RespEnvelope = { success?: unknown; msg?: unknown; obj?: unknown }; type RespEnvelope = { success?: unknown; msg?: unknown; obj?: unknown };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export class Msg<T = any> { export class Msg<T = any> {
success: boolean; success: boolean;
msg: string; msg: string;
@ -50,6 +50,7 @@ export class HttpUtil {
return typeof data === 'object' ? (data as Msg) : new Msg(false, 'unknown data:', data); return typeof data === 'object' ? (data as Msg) : new Msg(false, 'unknown data:', data);
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static async get<T = any>(url: string, params?: unknown, options: HttpOptions = {}): Promise<Msg<T>> { static async get<T = any>(url: string, params?: unknown, options: HttpOptions = {}): Promise<Msg<T>> {
const { silent, ...axiosOpts } = options; const { silent, ...axiosOpts } = options;
try { try {
@ -66,6 +67,7 @@ export class HttpUtil {
} }
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static async post<T = any>(url: string, data?: unknown, options: HttpOptions = {}): Promise<Msg<T>> { static async post<T = any>(url: string, data?: unknown, options: HttpOptions = {}): Promise<Msg<T>> {
const { silent, ...axiosOpts } = options; const { silent, ...axiosOpts } = options;
try { try {
@ -82,6 +84,7 @@ export class HttpUtil {
} }
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static async postWithModal<T = any>(url: string, data?: unknown, modal?: HttpModal | null): Promise<Msg<T>> { static async postWithModal<T = any>(url: string, data?: unknown, modal?: HttpModal | null): Promise<Msg<T>> {
if (modal) { if (modal) {
modal.loading(true); modal.loading(true);
@ -262,51 +265,53 @@ export class ObjectUtil {
} }
} }
static clone(obj: any): any { static clone<T>(obj: T): T {
if (obj instanceof Array) { if (obj instanceof Array) {
const newArr: unknown[] = []; const newArr: unknown[] = [];
this.copyArr(newArr, obj); this.copyArr(newArr, obj);
return newArr; return newArr as unknown as T;
} }
if (obj instanceof Object) { if (obj instanceof Object) {
const newObj: AnyRecord = {}; const newObj: AnyRecord = {};
const rec = obj as AnyRecord; const rec = obj as unknown as AnyRecord;
for (const key of Object.keys(rec)) { for (const key of Object.keys(rec)) {
newObj[key] = rec[key]; newObj[key] = rec[key];
} }
return newObj; return newObj as unknown as T;
} }
return obj; return obj;
} }
static deepClone(obj: any): any { static deepClone<T>(obj: T): T {
if (obj instanceof Array) { if (obj instanceof Array) {
const newArr: unknown[] = []; const newArr: unknown[] = [];
for (const item of obj) { for (const item of obj) {
newArr.push(this.deepClone(item)); newArr.push(this.deepClone(item));
} }
return newArr; return newArr as unknown as T;
} }
if (obj instanceof Object) { if (obj instanceof Object) {
const newObj: AnyRecord = {}; const newObj: AnyRecord = {};
const rec = obj as AnyRecord; const rec = obj as unknown as AnyRecord;
for (const key of Object.keys(rec)) { for (const key of Object.keys(rec)) {
newObj[key] = this.deepClone(rec[key]); newObj[key] = this.deepClone(rec[key]);
} }
return newObj; return newObj as unknown as T;
} }
return obj; return obj;
} }
static cloneProps(dest: any, src: any, ...ignoreProps: string[]): void { static cloneProps(dest: object, src: object, ...ignoreProps: string[]): void {
if (dest == null || src == null) return; if (dest == null || src == null) return;
const ignoreEmpty = this.isArrEmpty(ignoreProps); const ignoreEmpty = this.isArrEmpty(ignoreProps);
for (const key of Object.keys(src)) { const d = dest as AnyRecord;
if (!Object.prototype.hasOwnProperty.call(src, key)) continue; const s = src as AnyRecord;
if (!Object.prototype.hasOwnProperty.call(dest, key)) continue; for (const key of Object.keys(s)) {
if (src[key] === undefined) continue; if (!Object.prototype.hasOwnProperty.call(s, key)) continue;
if (!Object.prototype.hasOwnProperty.call(d, key)) continue;
if (s[key] === undefined) continue;
if (ignoreEmpty) { if (ignoreEmpty) {
dest[key] = src[key]; d[key] = s[key];
} else { } else {
let ignore = false; let ignore = false;
for (let i = 0; i < ignoreProps.length; ++i) { for (let i = 0; i < ignoreProps.length; ++i) {
@ -316,16 +321,17 @@ export class ObjectUtil {
} }
} }
if (!ignore) { if (!ignore) {
dest[key] = src[key]; d[key] = s[key];
} }
} }
} }
} }
static delProps(obj: any, ...props: string[]): void { static delProps(obj: object, ...props: string[]): void {
const o = obj as AnyRecord;
for (const prop of props) { for (const prop of props) {
if (prop in obj) { if (prop in o) {
delete obj[prop]; delete o[prop];
} }
} }
} }
@ -341,13 +347,18 @@ export class ObjectUtil {
return obj; return obj;
} }
static equals(a: any, b: any): boolean { static equals(a: unknown, b: unknown): boolean {
const aKeys = Object.keys(a); if (a == null || b == null || typeof a !== 'object' || typeof b !== 'object') {
const bKeys = Object.keys(b); return a === b;
}
const ra = a as AnyRecord;
const rb = b as AnyRecord;
const aKeys = Object.keys(ra);
const bKeys = Object.keys(rb);
if (aKeys.length !== bKeys.length) return false; if (aKeys.length !== bKeys.length) return false;
for (const key of aKeys) { for (const key of aKeys) {
if (!Object.prototype.hasOwnProperty.call(b, key)) return false; if (!Object.prototype.hasOwnProperty.call(rb, key)) return false;
if (a[key] !== b[key]) return false; if (ra[key] !== rb[key]) return false;
} }
return true; return true;
} }
@ -669,8 +680,7 @@ export class Utils {
let timeoutID: ReturnType<typeof setTimeout> | null = null; let timeoutID: ReturnType<typeof setTimeout> | null = null;
return function (this: unknown, ...args: A) { return function (this: unknown, ...args: A) {
if (timeoutID !== null) clearTimeout(timeoutID); if (timeoutID !== null) clearTimeout(timeoutID);
const self = this; timeoutID = setTimeout(() => fn.apply(this, args), delay);
timeoutID = setTimeout(() => fn.apply(self, args), delay);
}; };
} }
} }
@ -720,7 +730,11 @@ export interface ExpiryClient {
} }
export class ColorUtils { export class ColorUtils {
static usageColor(data: any, threshold: number, total: any): UsageColor { static usageColor(
data: number | null | undefined,
threshold: number,
total: number | { valueOf(): number } | null | undefined,
): UsageColor {
const t = Number(total ?? 0); const t = Number(total ?? 0);
const d = Number(data); const d = Number(data);
switch (true) { switch (true) {