From 3ad044de2f68a05885e56a40facded2a536092eb Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Mon, 20 Jul 2026 11:05:49 +0800 Subject: [PATCH] fix(plugin-view): coerce i18n tab-label helpers to string (TS2322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `useViewTabLabel()` (ViewTabBar) and `useViewLabel()` (ManageViewsDialog) returned the raw `t()` result, typed `string | object` by i18next. Their return values are rendered directly as React children and passed as aria-labels at ~35 sites, so vite build's dts pass emitted TS2322 (`string | object` not assignable to ReactNode). plugin-view has no type-check gate, so these did not fail the build — but if `t()` ever resolves a key to an object, React throws and white-screens. Coerce the resolved value with `String(v)` and annotate the helper's return type as `string`. Same "i18n/expression value is string|object, rendered raw → white-screen" class tracked separately from PR #2718. Co-Authored-By: Claude Opus 4.8 --- packages/plugin-view/src/ManageViewsDialog.tsx | 6 ++++-- packages/plugin-view/src/ViewTabBar.tsx | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/plugin-view/src/ManageViewsDialog.tsx b/packages/plugin-view/src/ManageViewsDialog.tsx index 08d55f1d6..1f3ead292 100644 --- a/packages/plugin-view/src/ManageViewsDialog.tsx +++ b/packages/plugin-view/src/ManageViewsDialog.tsx @@ -71,9 +71,11 @@ import type { ViewTabItem } from './ViewTabBar'; function useViewLabel() { try { const { t } = useObjectTranslation(); - return (key: string, fallback: string, vars?: Record) => { + return (key: string, fallback: string, vars?: Record): string => { const v = t(key, vars as any); - return !v || v === key ? fallback : v; + // i18next's `t()` is typed `string | object`; coerce so render sites and + // aria-labels always receive a string (an object child white-screens React). + return !v || v === key ? fallback : String(v); }; } catch { return (_k: string, fallback: string) => fallback; diff --git a/packages/plugin-view/src/ViewTabBar.tsx b/packages/plugin-view/src/ViewTabBar.tsx index 1e05ceab8..302b7888b 100644 --- a/packages/plugin-view/src/ViewTabBar.tsx +++ b/packages/plugin-view/src/ViewTabBar.tsx @@ -92,9 +92,11 @@ import { useObjectTranslation } from '@object-ui/react'; function useViewTabLabel() { try { const { t } = useObjectTranslation(); - return (key: string, fallback: string, vars?: Record) => { + return (key: string, fallback: string, vars?: Record): string => { const v = t(key, vars as any); - return !v || v === key ? fallback : v; + // i18next's `t()` is typed `string | object`; coerce so render sites and + // aria-labels always receive a string (an object child white-screens React). + return !v || v === key ? fallback : String(v); }; } catch { return (_k: string, fallback: string) => fallback;