Skip to content

fix(chat): handle non-array children in fallback text and Actions types#225

Open
Ehesp wants to merge 2 commits intovercel:mainfrom
Ehesp:fix-action-children
Open

fix(chat): handle non-array children in fallback text and Actions types#225
Ehesp wants to merge 2 commits intovercel:mainfrom
Ehesp:fix-action-children

Conversation

@Ehesp
Copy link

@Ehesp Ehesp commented Mar 11, 2026

Hello,

This PR fixes two related issues that showed up when using cards built with JSX or after serialization, and when nesting Actions([...]) inside Card({ children: [...] }).

  1. Runtime crash

For section and fields nodes, the fallback-text path assumed children was always an array and called child.children.map(...). After JSX or serialization, children can be a single value or undefined, which led to element.children.map is not a function.

  1. Type error when nesting Actions

Using the documented array form Actions([Button(...), Button(...)]) inside Card({ children: [...] }) caused a type error. The inner array was inferred as CardChild[], so it didn’t match the strict action-children overload and also didn’t match ContainerProps, giving: "Type 'ChatElement[]' has no properties in common with type 'ContainerProps'":

Essentially:

// No type errors, but runtime error
Actions({
  children: [
    Button({ id: "approve", label: "Approve", style: "primary" }),
    Button({ id: "reject", label: "Reject", style: "danger" }),
  ],
}),

// Type errors, but no runtime crash (and works as documented)
Actions([
    Button({ id: "approve", label: "Approve", style: "primary" }),
    Button({ id: "reject", label: "Reject", style: "danger" }),
]),

This PR does:

  1. Introduces getChildrenArray(node), which always returns an array: it keeps arrays as-is, wraps a single child in [child], and returns [] when children is missing or null. It uses it everywhere we iterate over children in the fallback-text path (in both cards.ts and markdown.ts’s BaseFormatConverter), so we never call .map on a non-array.

  2. Updates ActionsComponent so the array form is a single overload that accepts
    readonly CardChild[] | readonly ActionsChild[] | ActionsChild[]. That way Actions([...]) type-checks both on its own and when nested in Card({ children: [...] }), without adding overloads that would trigger the “unified type signatures” lint.

Test plan

Added tests to PR.

- Add getChildrenArray() to normalize children (array/single/undefined) for JSX/serialization
- Use getChildrenArray in cardToFallbackText and cardChildToFallbackText (cards.ts and markdown.ts BaseFormatConverter) to fix 'element.children.map is not a function'
- Unify ActionsComponent overloads so Actions([...]) type-checks when nested in Card({ children: [...] })
- Add tests for getChildrenArray and fallback text with non-array children

Made-with: Cursor
@vercel
Copy link
Contributor

vercel bot commented Mar 11, 2026

@Ehesp is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant