Skip to content

Conversation

@chriszarate
Copy link
Contributor

What?

Improve the type safety of Y.Map.

Why?

The typings for Y.Map have limitations. By default, the type of Y.Map values is the union of all possible values of the map. This type is accurate, but its non-specificity requires aggressive type narrowing or type casting / destruction with as—usually the latter. Preventable bugs sneak in.

How?

This PR introduces YMapWrap, which can infer the correct value type based on the provided key. It is just a type overlay and does not change the runtime behavior of Y.Map. Using this type, we can remove almost all of uses of as in the CRDT code.

We also benefit from more clearly representing the data structure encoded in the CRDT document. See YPostRecord and YBlockRecord.

Testing Instructions

These changes are covered by existing tests, but you can check out this branch and confirm the added type safety by trying to introduce type bugs.

Before

interface SomeRecord {
  foo: string;
  bar: number;
}

const doc = new Y.Doc();
const someRecordMap = doc.getMap( 'some_record' );

someRecordMap.set( 'foo', 1234 ); // no error!
someRecordMap.get( 'bar' ) // return type: string | number | undefined

After

interface SomeRecord {
  foo: string;
  bar: number;
}

const doc = new Y.Doc();
const someRecordMap = getMap< SomeRecord >( doc, 'some_record' );

someRecordMap.set( 'foo', 1234 ); // TS Error: Argument of type 'number' is not assignable....
someRecordMap.get( 'bar' ) // return type: number | undefined

@chriszarate chriszarate requested a review from nerrad as a code owner October 29, 2025 21:54
@chriszarate chriszarate added [Feature] Real-time Collaboration Phase 3 of the Gutenberg roadmap around real-time collaboration [Type] Experimental Experimental feature or API. labels Oct 29, 2025
@github-actions
Copy link

github-actions bot commented Oct 29, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: chriszarate <czarate@git.wordpress.org>
Co-authored-by: maxschmeling <maxschmeling@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions
Copy link

Flaky tests detected in 2ebf66f.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/18923171665
📝 Reported issues:

@chriszarate chriszarate force-pushed the improve/crdt-ymap-types branch from 2ebf66f to 0cfb380 Compare October 31, 2025 16:19
Comment on lines 73 to 77
export function isYMap< T extends YMapRecord >(
value: unknown
): value is YMapWrap< T > {
return value instanceof Y.Map;
}
Copy link

@maxschmeling maxschmeling Oct 31, 2025

Choose a reason for hiding this comment

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

Should we have any concern that this type guard could get used by someone with the assumption that it somehow is doing a check for the actual type T when you can actually just pass any T type and as long as it's a Y.Map it will return true? Should we give it a name that indicates it isn't actually checking the generic type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Generic types are never checked. It's impossible to do run-time type checks in a type. They are always just pass-through. Open to another name if you think this isn't clear

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the good question, I pushed up 2d2e90b which makes it a much stricter type guard

@chriszarate chriszarate merged commit bb128f8 into wpvip/rtc-plugin Oct 31, 2025
35 checks passed
@chriszarate chriszarate deleted the improve/crdt-ymap-types branch October 31, 2025 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Real-time Collaboration Phase 3 of the Gutenberg roadmap around real-time collaboration [Type] Experimental Experimental feature or API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants