TypeScript unknown in JSX && Expressions
Problem
When using Record<string, unknown> (or any unknown-typed values) in JSX conditional
rendering with &&, TypeScript raises:
Type 'unknown' is not assignable to type 'string | number | bigint | boolean |
ReactElement<...> | Iterable<ReactNode> | ReactPortal | Promise<...> | null | undefined'
Even when the value is wrapped in String(), the error still occurs because the
JSX expression itself evaluates to unknown | JSX.Element.
Context / Trigger Conditions
- Object is typed as
Record<string, unknown>(common with JSON fields from APIs) - JSX contains:
{obj.field && <Component value={String(obj.field)} />} - Error TS2322 on the
&&line, not inside the Component - TypeScript strict mode enabled
Root Cause
In TypeScript, a && b returns the type of a when a is falsy — so unknown && JSX.Element
resolves to unknown | JSX.Element. TypeScript cannot prove unknown is a valid ReactNode,
so it rejects the expression.
Solution
Force a boolean result with !! before the &&:
// ❌ FAILS — wd.entry_time is unknown
{wd.entry_time && (
<DetailField value={String(wd.entry_time)} />
)}
// ✅ WORKS — !!wd.entry_time is boolean
{!!wd.entry_time && (
<DetailField value={String(wd.entry_time)} />
)}
// Also works with null check
{wd.entry_time != null && (
<DetailField value={String(wd.entry_time)} />
)}
For chained &&:
// ❌ FAILS — first && returns unknown
{wd.needs_reentry && wd.reentry_date && (
<DetailField value={String(wd.reentry_date)} />
)}
// ✅ WORKS — both forced to boolean
{!!wd.needs_reentry && !!wd.reentry_date && (
<DetailField value={String(wd.reentry_date)} />
)}
Verification
yarn frontend:typecheck # or tsc --noEmit
# Should produce no TS2322 errors on the affected lines
Notes
- This is not a React issue — it's a TypeScript type-narrowing limitation with
unknown String(unknown)returnsstring(which is valid ReactNode), but the issue is the JSX expression type beforeString()is applied- Alternative: cast the whole object with a more specific type at the call site
const wd = (r.workDetails ?? {}) as Record<string, string | boolean | undefined>— this eliminates all!!guards but reduces type safety !!is the idiomatic JS/TS way to coerce any value to boolean