Skip to content

fix(types): correct BlockRule value type from any[] to PortableTextBlock#12509

Open
jordanl17 wants to merge 1 commit intomainfrom
gh-4464/fix-block-rule-typing
Open

fix(types): correct BlockRule value type from any[] to PortableTextBlock#12509
jordanl17 wants to merge 1 commit intomainfrom
gh-4464/fix-block-rule-typing

Conversation

@jordanl17
Copy link
Copy Markdown
Member

Description

Fixes #4464.

BlockRule and BlockDefinition used any[] as their value type, but a block's runtime value is a single PortableTextBlock object - not an array. Every other schema type (StringRule, NumberRule, ObjectRule) uses its correct singular value type. This change makes block consistent.

Backwards compatibility

This is a type-level breaking change by strict semver standards: custom() validators on block types will now receive PortableTextBlock instead of any[]. In practice, the risk is low:

  • The any[] type was wrong - it never matched the runtime value. Anyone calling array methods based on this type already had a latent runtime bug.
  • any is maximally permissive, so most real-world validators access object properties on value rather than treating it as an array. That code continues to work.
  • The test that accompanied the original typing used value?.filter(...) as if it were an array - confirming the type was incorrect, not that array usage was intended.

What to review

  • BlockRule now extends RuleDef<BlockRule, PortableTextBlock> instead of RuleDef<BlockRule, any[]>
  • BlockDefinition.initialValue and BlockDefinition.validation use PortableTextBlock instead of any[]
  • Promoted PortableTextBlock and its constituent types (PortableTextTextBlock, PortableTextObject, PortableTextSpan, PortableTextChild, PortableTextListBlock) from @alpha to @public - these types have been stable since September 2020 and are already widely used by consumers

Testing

Notes for release

BlockRule now uses PortableTextBlock instead of any[], matching the actual runtime value passed to custom validators.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
page-building-studio Ready Ready Preview, Comment Mar 25, 2026 9:55am
test-studio Ready Ready Preview, Comment Mar 25, 2026 9:55am

Request Review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 25, 2026

🧪 E2E Preview environment

🔑 Environment Variables for Local Testing

This is the preview URL for the E2E tests: https://e2e-studio-k5tx8ou8z.sanity.dev

To run the E2E tests locally, you can use the following environment variables, then run pnpm test:e2e --ui to open the Playwright test runner.

💬 Remember to build the project first with pnpm build:e2e.

  SANITY_E2E_PROJECT_ID=ittbm412
  SANITY_E2E_BASE_URL=https://e2e-studio-k5tx8ou8z.sanity.dev
  SANITY_E2E_DATASET="update depending the project you want to test (pr-12509-chromium-23535067655 || pr-12509-firefox-23535067655 )"
  SANITY_E2E_DATASET_CHROMIUM=pr-12509-chromium-23535067655
  SANITY_E2E_DATASET_FIREFOX=pr-12509-firefox-23535067655

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 25, 2026

📊 Playwright Test Report

Download Full E2E Report

This report contains test results, including videos of failing tests.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 25, 2026

📦 Bundle Stats — sanity

Compared against main (d5c2b1ca) · v5.18.0 (npm)

Metric Value vs main (d5c2b1c) vs v5.18.0
Internal (raw) 4.37 MB - +27 B, +0.0%
Internal (gzip) 1.00 MB - +3 B, +0.0%
Bundled (raw) 12.04 MB - -36.1 KB, -0.3%
Bundled (gzip) 2.71 MB - -6.3 KB, -0.2%
Import time 1.56s -4ms, -0.3% +53ms, +3.5%
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Treemap artifacts are attached to the CI run for detailed size analysis
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 25, 2026

⚡️ Editor Performance Report

Updated Wed, 25 Mar 2026 10:05:13 GMT

Benchmark reference
latency of sanity@latest
experiment
latency of this branch
Δ (%)
latency difference
arrayI18n (simple-en) 76.9 efps (13ms) 90.9 efps (11ms) -2ms (-/-%)
article (title) 35.1 efps (29ms) 31.7 efps (32ms) +3ms (+10.5%)
article (body) 45.8 efps (22ms) 44.1 efps (23ms) +1ms (+3.9%)
article (string inside object) 48.8 efps (21ms) 43.5 efps (23ms) +3ms (+12.2%)
article (string inside array) 47.6 efps (21ms) 45.5 efps (22ms) +1ms (+4.8%)
recipe (name) 99.9+ efps (9ms) 99.9+ efps (10ms) +1ms (-/-%)
recipe (description) 52.6 efps (19ms) 55.6 efps (18ms) -1ms (-5.3%)
recipe (instructions) 99.9+ efps (5ms) 99.9+ efps (5ms) +0ms (-/-%)
singleString (stringField) 99.9+ efps (6ms) 99.9+ efps (6ms) +0ms (-/-%)
synthetic (title) 58.8 efps (17ms) 58.8 efps (17ms) +0ms (-/-%)
synthetic (string inside object) 62.5 efps (16ms) 62.5 efps (16ms) +0ms (-/-%)

efps — editor "frames per second". The number of updates assumed to be possible within a second.

Derived from input latency. efps = 1000 / input_latency

Detailed information

🏠 Reference result

The performance result of sanity@latest

Benchmark latency p75 p90 p99 blocking time test duration
arrayI18n (simple-en) 13ms 15ms 17ms 29ms 0ms 5.4s
article (title) 29ms 31ms 38ms 59ms 84ms 7.6s
article (body) 22ms 39ms 71ms 120ms 238ms 6.1s
article (string inside object) 21ms 23ms 29ms 64ms 0ms 6.3s
article (string inside array) 21ms 25ms 37ms 78ms 5ms 6.6s
recipe (name) 9ms 11ms 14ms 35ms 0ms 5.2s
recipe (description) 19ms 22ms 27ms 33ms 0ms 4.5s
recipe (instructions) 5ms 9ms 10ms 19ms 0ms 3.0s
singleString (stringField) 6ms 9ms 12ms 22ms 0ms 4.4s
synthetic (title) 17ms 25ms 64ms 104ms 1225ms 9.5s
synthetic (string inside object) 16ms 17ms 31ms 82ms 1153ms 8.6s

🧪 Experiment result

The performance result of this branch

Benchmark latency p75 p90 p99 blocking time test duration
arrayI18n (simple-en) 11ms 13ms 16ms 24ms 0ms 5.2s
article (title) 32ms 36ms 63ms 75ms 63ms 8.8s
article (body) 23ms 28ms 39ms 97ms 191ms 5.5s
article (string inside object) 23ms 28ms 37ms 74ms 35ms 6.7s
article (string inside array) 22ms 28ms 30ms 50ms 0ms 6.7s
recipe (name) 10ms 12ms 14ms 35ms 0ms 5.1s
recipe (description) 18ms 20ms 22ms 48ms 0ms 4.3s
recipe (instructions) 5ms 9ms 10ms 14ms 0ms 3.0s
singleString (stringField) 6ms 9ms 11ms 19ms 0ms 4.5s
synthetic (title) 17ms 21ms 41ms 128ms 1229ms 9.4s
synthetic (string inside object) 16ms 17ms 22ms 100ms 1198ms 8.7s

📚 Glossary

column definitions

  • benchmark — the name of the test, e.g. "article", followed by the label of the field being measured, e.g. "(title)".
  • latency — the time between when a key was pressed and when it was rendered. derived from a set of samples. the median (p50) is shown to show the most common latency.
  • p75 — the 75th percentile of the input latency in the test run. 75% of the sampled inputs in this benchmark were processed faster than this value. this provides insight into the upper range of typical performance.
  • p90 — the 90th percentile of the input latency in the test run. 90% of the sampled inputs were faster than this. this metric helps identify slower interactions that occurred less frequently during the benchmark.
  • p99 — the 99th percentile of the input latency in the test run. only 1% of sampled inputs were slower than this. this represents the worst-case scenarios encountered during the benchmark, useful for identifying potential performance outliers.
  • blocking time — the total time during which the main thread was blocked, preventing user input and UI updates. this metric helps identify performance bottlenecks that may cause the interface to feel unresponsive.
  • test duration — how long the test run took to complete.

@github-actions
Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 39.56% 25925 / 65526
🔵 Statements 32.03% 33665 / 105098
🔵 Functions 28.34% 5223 / 18428
🔵 Branches 24.47% 20489 / 83719
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/@sanity/types/src/portableText/types.ts 0% 0% 0% 0%
packages/@sanity/types/src/schema/definition/type/block.ts 0% 0% 0% 0%
Generated in workflow #54027 for commit b91d8b5 by the Vitest Coverage Report Action

@jordanl17 jordanl17 marked this pull request as ready for review March 25, 2026 10:08
@jordanl17 jordanl17 requested a review from a team as a code owner March 25, 2026 10:08
@jordanl17 jordanl17 requested review from pedrobonamin and removed request for a team March 25, 2026 10:08
marks?: BlockMarksDefinition
of?: ArrayOfType<'object' | 'reference'>[]
initialValue?: InitialValueProperty<any, any[]>
initialValue?: InitialValueProperty<any, PortableTextBlock>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes make sense, but is this actually valid?
I tried it and we surface an error when adding an initial value to the block definition.
I added this in AllTheBellsAndWhistles

        defineArrayMember({
          type: 'block',
          name: 'block',
          title: 'Block',

+          initialValue: {
+           style: 'normal',
+            _type: 'block',
+           _key: '123',
+           children: [
+              {
+                _type: 'span',
+                text: 'Hello, world!',
+              },
+            ],
+          },

Typescript validates it, but then the studio opens with an error:

Image
# Schema errors

There were errors while attempting to compile the configuration of your Sanity Studio Schema types.

## Document type "pt_allTheBellsAndWhistles"

Path: pt_allTheBellsAndWhistles:document → fields → text:array → of → block:block
Error: Found unknown properties for block declaration: "initialValue"

## Document type "pt_allTheBellsAndWhistles"

Path: pt_allTheBellsAndWhistles:document → fields → text:array → of → block:block
Error: Found unknown properties for block declaration: "initialValue"

```

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.

Why is block typed as any[]?

2 participants