From 0aaeebd8042facb4e5d1a1e915f484c7c0c0dc82 Mon Sep 17 00:00:00 2001 From: R4vager Date: Wed, 15 Apr 2026 14:00:31 -0400 Subject: [PATCH] renderer-three, ui: drop 'as unknown as' double-casts by widening callee signatures Two callsites were forced into 'as unknown as X' shapes because the target function took a narrower parameter than needed: - packages/renderer-three/src/three-scene-basics.ts:207 passed value as unknown as Record to the private helper isPlainGeometraThreeSceneBasicsOptionsRecord, because the outer type guard had already narrowed value to PlainGeometraThreeViewSizingState and TS refused the single-step cast to Record. - packages/ui/src/theme.ts:244 laundered peekTheme() and the partial through Record in both directions just to satisfy deepMerge's Record-typed signature, then re-asserted the result as Theme. Widens isPlainGeometraThreeSceneBasicsOptionsRecord to take 'unknown' and handle the null/object narrowing internally (one contained cast inside the guard). Both public wrappers now call it with no casts and their behavior is unchanged. Also collapses the redundant null/object precheck in isPlainGeometraThreeSceneBasicsOptions now that the helper owns it. Makes deepMerge generic over '' so mergeTheme collapses to 'deepMerge(peekTheme(), partial)' with zero casts at the callsite. The internal Record casts inside deepMerge are standard 'I know this shape better than the compiler' implementation details, and stay contained to the helper body. Full workspace build + fast vitest suite (2360 tests) pass. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../renderer-three/src/three-scene-basics.ts | 9 +++++---- packages/ui/src/theme.ts | 19 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/renderer-three/src/three-scene-basics.ts b/packages/renderer-three/src/three-scene-basics.ts index ab594acc..19fab3e7 100644 --- a/packages/renderer-three/src/three-scene-basics.ts +++ b/packages/renderer-three/src/three-scene-basics.ts @@ -168,7 +168,9 @@ function isPlainCameraPosition(value: unknown): value is THREE.Vector3Tuple { ) } -function isPlainGeometraThreeSceneBasicsOptionsRecord(o: Record): boolean { +function isPlainGeometraThreeSceneBasicsOptionsRecord(value: unknown): boolean { + if (value === null || typeof value !== 'object') return false + const o = value as Record if (typeof o.threeBackgroundHex !== 'number' || !Number.isFinite(o.threeBackgroundHex)) { return false } @@ -192,8 +194,7 @@ function isPlainGeometraThreeSceneBasicsOptionsRecord(o: Record export function isPlainGeometraThreeSceneBasicsOptions( value: unknown, ): value is PlainGeometraThreeSceneBasicsOptions { - if (value === null || typeof value !== 'object') return false - return isPlainGeometraThreeSceneBasicsOptionsRecord(value as Record) + return isPlainGeometraThreeSceneBasicsOptionsRecord(value) } /** @@ -204,7 +205,7 @@ export function isPlainGeometraThreeSceneBasicsOptions( */ export function isPlainGeometraThreeHostSnapshot(value: unknown): value is PlainGeometraThreeHostSnapshot { if (!isPlainGeometraThreeViewSizingState(value)) return false - return isPlainGeometraThreeSceneBasicsOptionsRecord(value as unknown as Record) + return isPlainGeometraThreeSceneBasicsOptionsRecord(value) } /** diff --git a/packages/ui/src/theme.ts b/packages/ui/src/theme.ts index c61a2210..f566d54f 100644 --- a/packages/ui/src/theme.ts +++ b/packages/ui/src/theme.ts @@ -223,25 +223,24 @@ export type DeepPartial = { [K in keyof T]?: T[K] extends object ? DeepPartial : T[K] } -function deepMerge(base: Record, partial: Record): Record { - const result = { ...base } - for (const key of Object.keys(partial)) { - const val = partial[key] +function deepMerge(base: T, partial: DeepPartial): T { + const result = { ...base } as Record + const src = partial as Record + for (const key of Object.keys(src)) { + const val = src[key] if (val !== undefined && typeof val === 'object' && val !== null && !Array.isArray(val)) { - result[key] = deepMerge( - (base[key] ?? {}) as Record, - val as Record, - ) + const existing = (result[key] ?? {}) as object + result[key] = deepMerge(existing, val as DeepPartial) } else if (val !== undefined) { result[key] = val } } - return result + return result as T } /** Deep-merge a partial override onto the current theme. Returns a new `Theme`. */ export function mergeTheme(partial: DeepPartial): Theme { - return deepMerge(peekTheme() as unknown as Record, partial as unknown as Record) as unknown as Theme + return deepMerge(peekTheme(), partial) } // ---------------------------------------------------------------------------