Skip to content

feat: add type guard helpers for ContentBlock variants#975

Open
ac12644 wants to merge 1 commit intoanthropics:mainfrom
ac12644:feat/content-block-type-guards
Open

feat: add type guard helpers for ContentBlock variants#975
ac12644 wants to merge 1 commit intoanthropics:mainfrom
ac12644:feat/content-block-type-guards

Conversation

@ac12644
Copy link
Copy Markdown

@ac12644 ac12644 commented Apr 4, 2026

Summary

  • Adds 12 type guard functions (isTextBlock, isToolUseBlock, isThinkingBlock, etc.) that narrow ContentBlock to its specific variant
  • Exported from the package root for ergonomic usage
  • Includes compile-time exhaustiveness test that fails if a new ContentBlock variant is added without a corresponding guard

Motivation

Addresses #432 — the most upvoted open issue (15+ thumbs up). Since v0.22.0, ContentBlock is a 12-member union, so msg.content[0].text requires type narrowing. Users currently resort to as casts or // @ts-ignore.

With this change:

import { isTextBlock } from '@anthropic-ai/sdk';

const message = await client.messages.create({ ... });
const textBlocks = message.content.filter(isTextBlock);
console.log(textBlocks[0].text); // fully typed, no casts

Design decisions

  • Type guards over overloads: Overloading create() based on tools presence would be type-unsound (thinking, server tools, etc. can produce non-text blocks even without tools). Type guards are correct and composable.
  • src/lib/ placement: Hand-written helpers that survive Stainless code generation, following the existing pattern (parser.ts, MessageStream.ts).
  • Exhaustiveness test: Uses compile-time conditional types (same pattern as TracksToolInput.test.ts) so adding a new ContentBlock variant without a guard is a build error.

Test plan

  • yarn lint passes (types + eslint + prettier)
  • 18 Jest tests pass (runtime behavior + type narrowing for all 12 guards)
  • Compile-time exhaustiveness check covers all 12 variants
  • Existing tests unaffected (TracksToolInput, index.test, parser.test)
  • Full build passes (CJS + ESM)
  • Verified imports work from built dist (require and import)

Closes #432

Adds 12 type guard functions (isTextBlock, isToolUseBlock, etc.) that
narrow ContentBlock to its specific variant. Exported from the package
root so users can write:

  import { isTextBlock } from '@anthropic-ai/sdk';
  const textBlocks = message.content.filter(isTextBlock);

Includes compile-time exhaustiveness test that fails if a new
ContentBlock variant is added without a corresponding guard.

Closes anthropics#432
@ac12644 ac12644 requested a review from a team as a code owner April 4, 2026 23:19
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.

[Types] Error: Property 'text' does not exist on type 'ContentBlock'.

1 participant