Skip to content

[lexical-website] Documentation Update: Describe type-signature breaks in CHANGELOG.md by the call shapes they break, and add the missing v0.48 list Backspace behavior change - #9728

Merged
zurfyx merged 2 commits into
mainfrom
zurfyx/changelog-type-breaks
Nov 4, 2026

Conversation

@zurfyx

@zurfyx zurfyx commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Description

Three backfilled CHANGELOG.md entries did not tell people upgrading who is affected or what to change:

To keep this from recurring, .changeset/README.md gets a "Type signature changes" section: a type change that can make existing call sites stop compiling is [breaking] even when it only deprecates something, and its Detect names the call shapes, including callers that rely on an inferred type argument. AGENTS.md and CONTRIBUTING.md, which already summarize the changeset format, get one paragraph each. pnpm run check-changesets now warns (without failing) when a [deprecation] or [chore] entry mentions a type parameter, overload, generic, return or payload type, or signature and has no Detect: paragraph. The pattern also covers "type params", "branded", "invariant", "narrowed return/payload" and "now returns". Neither the pending changesets nor the curated [deprecation] and Other entries in CHANGELOG.md trigger it.

Out of scope: packages/lexical-list/flow/LexicalList.js.flow still declares collapseAtStart(selection: RangeSelection): true. Changing it is a published Flow type change that needs its own changeset.

Changeset

None needed: documentation and changeset tooling only.

Test plan

The new CHANGELOG.md wording was checked against the declarations at the v0.45.0, v0.46.0, v0.48.0 and v0.49.0 tags, and the TypeScript errors and the Migration snippets were compiled against the current sources. The list Backspace change was checked with a scratch unit test (not committed) that presses Backspace at the start of the second of three items, with and without ListExtension: ["list:first","paragraph:second","list:third"] with it, and ["list:firstsecond|third"] with rich text alone, which is what v0.47 did.

Before

With the test changes applied but not the CHANGELOG.md and entry.mjs changes:

$ pnpm vitest run scripts/__tests__/unit/changesets.test.ts scripts/__tests__/unit/changelog.test.ts
     × warns when a deprecation or chore changes types without Detect 4ms
     × parses the repository CHANGELOG.md 15ms
 FAIL  |scripts-unit| scripts/__tests__/unit/changelog.test.ts > parseChangelog > parses the repository CHANGELOG.md
AssertionError: expected { behavior: 43, breaking: 35, …(1) } to deeply equal { behavior: 44, breaking: 35, …(1) }
 FAIL  |scripts-unit| scripts/__tests__/unit/changesets.test.ts > changeset entry format > warns when a deprecation or chore changes types without Detect
AssertionError: expected { errors: [], warnings: [] } to deeply equal { errors: [], warnings: [ …(1) ] }
 Test Files  2 failed (2)
      Tests  2 failed | 63 passed (65)

After

$ pnpm vitest run scripts/__tests__/unit/changesets.test.ts scripts/__tests__/unit/changelog.test.ts
 Test Files  2 passed (2)
      Tests  65 passed (65)

$ pnpm run check-changesets
Changeset check passed: 80 pending changeset(s) valid.

Prettier and ESLint pass on the changed files.

@vercel

vercel Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
lexical Ready Ready Preview Nov 4, 2026 3:52pm UTC
lexical-playground Ready Ready Preview Nov 4, 2026 3:52pm UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 2, 2026
@meta-code-review

meta-code-review Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 4, 2026, 4:06 PM UTC

@pkg-pr-new

pkg-pr-new Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
lexical

npm i https://pkg.pr.new/lexical@9728

@lexical/a11y

npm i https://pkg.pr.new/@lexical/a11y@9728

@lexical/clipboard

npm i https://pkg.pr.new/@lexical/clipboard@9728

@lexical/code

npm i https://pkg.pr.new/@lexical/code@9728

@lexical/code-core

npm i https://pkg.pr.new/@lexical/code-core@9728

@lexical/code-prism

npm i https://pkg.pr.new/@lexical/code-prism@9728

@lexical/code-shiki

npm i https://pkg.pr.new/@lexical/code-shiki@9728

@lexical/compiler

npm i https://pkg.pr.new/@lexical/compiler@9728

@lexical/devtools-core

npm i https://pkg.pr.new/@lexical/devtools-core@9728

@lexical/dragon

npm i https://pkg.pr.new/@lexical/dragon@9728

@lexical/eslint-plugin

npm i https://pkg.pr.new/@lexical/eslint-plugin@9728

@lexical/extension

npm i https://pkg.pr.new/@lexical/extension@9728

@lexical/fast-check

npm i https://pkg.pr.new/@lexical/fast-check@9728

@lexical/file

npm i https://pkg.pr.new/@lexical/file@9728

@lexical/hashtag

npm i https://pkg.pr.new/@lexical/hashtag@9728

@lexical/headless

npm i https://pkg.pr.new/@lexical/headless@9728

@lexical/history

npm i https://pkg.pr.new/@lexical/history@9728

@lexical/html

npm i https://pkg.pr.new/@lexical/html@9728

@lexical/internal

npm i https://pkg.pr.new/@lexical/internal@9728

@lexical/link

npm i https://pkg.pr.new/@lexical/link@9728

@lexical/list

npm i https://pkg.pr.new/@lexical/list@9728

@lexical/mark

npm i https://pkg.pr.new/@lexical/mark@9728

@lexical/markdown

npm i https://pkg.pr.new/@lexical/markdown@9728

@lexical/mdast

npm i https://pkg.pr.new/@lexical/mdast@9728

@lexical/offset

npm i https://pkg.pr.new/@lexical/offset@9728

@lexical/overflow

npm i https://pkg.pr.new/@lexical/overflow@9728

@lexical/plain-text

npm i https://pkg.pr.new/@lexical/plain-text@9728

@lexical/react

npm i https://pkg.pr.new/@lexical/react@9728

@lexical/rich-text

npm i https://pkg.pr.new/@lexical/rich-text@9728

@lexical/selection

npm i https://pkg.pr.new/@lexical/selection@9728

@lexical/table

npm i https://pkg.pr.new/@lexical/table@9728

@lexical/tailwind

npm i https://pkg.pr.new/@lexical/tailwind@9728

@lexical/text

npm i https://pkg.pr.new/@lexical/text@9728

@lexical/utils

npm i https://pkg.pr.new/@lexical/utils@9728

@lexical/y

npm i https://pkg.pr.new/@lexical/y@9728

@lexical/yjs

npm i https://pkg.pr.new/@lexical/yjs@9728

commit: 01be899

…s in CHANGELOG.md by the call shapes they break, and add the missing v0.48 list Backspace behavior change

## Description

Three backfilled CHANGELOG.md entries did not tell people upgrading who is affected or what to change:

- v0.46.0, #8661: the breaking entry said traversal methods "return the base node type" and only named annotated assignments. It did not say that the methods (and `$getNodeByKey` / `$getNodeByKeyOrThrow`) gained a non-generic first overload, so a call without a type argument that used to infer `T` from where its result went (a function argument, a return value, an annotated variable or array) now returns `LexicalNode` and stops compiling. Detect now names those call shapes and the TypeScript error, Migration shows the function-argument case, and the matching deprecation entry now has its own Detect and says the overloads were removed from TypeScript in v0.53.0 (#9479), while the Flow declarations keep them after #9529, instead of "Removal: not scheduled".
- v0.49.0, #8877: the entry said `LexicalCommand<T>` is invariant, but Detect only named `LexicalCommand<unknown>` and explicit generics. A `registerCommand` listener whose parameter annotation is narrower or wider than the payload (for example `(event: KeyboardEvent) => ...` for `KEY_ENTER_COMMAND`, whose payload is `KeyboardEvent | null`), or a helper typed `LexicalCommand<KeyboardEvent>` called with that command, also stops compiling. Detect lists these shapes and the error, and Migration shows inferred listeners and a generic helper.
- v0.48.0, #8829: not listed at all. `registerList` handles Backspace at `COMMAND_PRIORITY_BEFORE_EDITOR`, before rich text: at the start of a top-level item it turns the item into a paragraph and splits the list, where it used to join it to the item above. It is now a behavior change with a Verify line. `ListItemNode.collapseAtStart` also widened its return type from `true` to `boolean`; the entry says why that is not filed as breaking.

To keep this from recurring, `.changeset/README.md` gets a "Type signature changes" section: a type change that can make existing call sites stop compiling is `[breaking]` even when it only deprecates something, and its Detect names the call shapes, including callers that rely on an inferred type argument. AGENTS.md and CONTRIBUTING.md, which already summarize the changeset format, get one paragraph each. `pnpm run check-changesets` now warns (without failing) when a `[deprecation]` or `[chore]` entry mentions a type parameter, overload, generic, return or payload type, or signature and has no `Detect:` paragraph. The pattern also covers "type params", "branded", "invariant", "narrowed return/payload" and "now returns". Neither the pending changesets nor the curated `[deprecation]` and Other entries in CHANGELOG.md trigger it.

Out of scope: `packages/lexical-list/flow/LexicalList.js.flow` still declares `collapseAtStart(selection: RangeSelection): true`. Changing it is a published Flow type change that needs its own changeset.

## Changeset

None needed: documentation and changeset tooling only.

## Test plan

The new CHANGELOG.md wording was checked against the declarations at the v0.45.0, v0.46.0, v0.48.0 and v0.49.0 tags, and the TypeScript errors and the Migration snippets were compiled against the current sources. The list Backspace change was checked with a scratch unit test (not committed) that presses Backspace at the start of the second of three items, with and without `ListExtension`: `["list:first","paragraph:second","list:third"]` with it, and `["list:firstsecond|third"]` with rich text alone, which is what v0.47 did.

### Before

With the test changes applied but not the CHANGELOG.md and `entry.mjs` changes:

```
$ pnpm vitest run scripts/__tests__/unit/changesets.test.ts scripts/__tests__/unit/changelog.test.ts
     × warns when a deprecation or chore changes types without Detect 4ms
     × parses the repository CHANGELOG.md 15ms
 FAIL  |scripts-unit| scripts/__tests__/unit/changelog.test.ts > parseChangelog > parses the repository CHANGELOG.md
AssertionError: expected { behavior: 43, breaking: 35, …(1) } to deeply equal { behavior: 44, breaking: 35, …(1) }
 FAIL  |scripts-unit| scripts/__tests__/unit/changesets.test.ts > changeset entry format > warns when a deprecation or chore changes types without Detect
AssertionError: expected { errors: [], warnings: [] } to deeply equal { errors: [], warnings: [ …(1) ] }
 Test Files  2 failed (2)
      Tests  2 failed | 63 passed (65)
```

### After

```
$ pnpm vitest run scripts/__tests__/unit/changesets.test.ts scripts/__tests__/unit/changelog.test.ts
 Test Files  2 passed (2)
      Tests  65 passed (65)

$ pnpm run check-changesets
Changeset check passed: 80 pending changeset(s) valid.
```

Prettier and ESLint pass on the changed files.

@meta-code-review meta-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This looks good. The backfilled entries are much more useful to people upgrading, and the #8661 and #8877 call shapes and Migration snippets hold up against the old and new declarations. A few optional, non-blocking suggestions, mostly wording:

  1. CHANGELOG.md:988-990 (v0.48.0 Backspace entry). Nested list items were already outdented before #8829: @lexical/rich-text outdents an indented block when Backspace is pressed at its start ($isSelectionCollapsedAtFrontOfIndentedBlock), and a nested ListItemNode reports its depth as its indent. What changed is top-level items. That includes the first item when a paragraph comes before the list: it used to join that paragraph, and now it becomes a paragraph of its own. Suggested wording: "Before, Backspace at the start of a top-level item joined it to the end of the block above (the previous item, or a paragraph before the list); that now takes a second Backspace. Nested items were already outdented by @lexical/rich-text."
  2. CHANGELOG.md:1144 and .changeset/README.md:128. The quoted Type 'LexicalNode' is not assignable to type ... is not what the listed shapes report. They report Type 'LexicalNode | null' is not assignable ... or Type 'LexicalNode[]' ... (TS2322), Argument of type 'LexicalNode' is not assignable ... (TS2345), and, for a return value, TS2740 Type 'LexicalNode' is missing the following properties from type .... The new "Type signature changes" section asks every Detect: to quote the error, so it would help to list TS2740 and quote the messages in a form people can search their build output for.
  3. .changeset/README.md:126 and :134. $styleList(root.getFirstChild()) already failed under strict before the overload change, because getFirstChild<T>() returns T | null. Using getFirstChildOrThrow(), as the CHANGELOG entry does, would make the example show a call that really did stop compiling.
  4. CHANGELOG.md:1254. "The Flow declarations keep them" reads as if Flow never lost the overloads. The v0.53.0 entry says they were removed from TypeScript and Flow, and #9529 brings them back in Flow in the next release.
  5. scripts/changesets/entry.mjs:110. invariant also matches Lexical's own invariant() helper, so a chore such as "Replace thrown errors with invariant" is told it may be [breaking]. Dropping that alternative still catches the #8877 case through generics and branded.

Leaving the Flow collapseAtStart declaration to its own change, as the description says, makes sense.

Comment thread CHANGELOG.md Resolved
Comment thread CHANGELOG.md Resolved
Comment thread .changeset/README.md Resolved
…raversal and v0.48 list Backspace changelog entries

## Description

Review follow-ups for the changelog entries this pull request rewrites:

- v0.48.0, #8829: nested list items were already outdented by `@lexical/rich-text` before #8829 (a nested `ListItemNode` reports its depth as its indent). The entry now says what changed: Backspace at the start of a top-level item used to join it to the block above, either the previous item or a paragraph before the list, and now turns it into a paragraph.
- v0.46.0, #8661: Detect quotes the TypeScript errors each call shape produces (TS2322, TS2345, and TS2740 for a return value).
- `.changeset/README.md`: the type-signature example uses `getFirstChildOrThrow()`, since `getFirstChild()` returns `T | null` and its argument case already failed under `strict`.

## Test plan

The quoted errors come from compiling the four call shapes (annotated `getFirstChild()`, annotated `getChildren()`, `getFirstChildOrThrow()` as an argument, and as a return value) against the current declarations, whose non-generic overloads are the ones v0.46.0 added:

```
t.ts(5,7): error TS2322: Type 'LexicalNode | null' is not assignable to type 'ParagraphNode | null'.
t.ts(6,7): error TS2322: Type 'LexicalNode[]' is not assignable to type 'ListItemNode[]'.
t.ts(7,12): error TS2345: Argument of type 'LexicalNode' is not assignable to parameter of type 'ListNode'.
t.ts(9,3): error TS2740: Type 'LexicalNode' is missing the following properties from type 'ListNode': __tag, __start, __listType, getTag, and 68 more.
```

A scratch unit test (not committed) pressing Backspace at the start of the first item of a list that follows a paragraph prints `["paragraph:para","paragraph:first","list:second"]` with `ListExtension` and `["paragraph:parafirst","list:second"]` with rich text alone.

### Before

Same tests as the previous commit:

```
$ pnpm vitest run scripts/__tests__/unit/changesets.test.ts scripts/__tests__/unit/changelog.test.ts
 Test Files  2 passed (2)
      Tests  65 passed (65)
```

### After

```
$ pnpm vitest run scripts/__tests__/unit/changesets.test.ts scripts/__tests__/unit/changelog.test.ts
 Test Files  2 passed (2)
      Tests  65 passed (65)

$ pnpm run check-changesets
Changeset check passed: 80 pending changeset(s) valid.
```

@meta-code-review meta-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This looks good: the backfilled entries now say who is affected and what to change, and the call shapes and Migration snippets in the #8661 and #8877 entries hold up against the released declarations. A few optional, non-blocking suggestions:

  1. CHANGELOG.md:1258 (v0.46.0 deprecation, Removal:). "The Flow declarations keep them, still deprecated, after #9529" reads as if Flow never lost the overloads. v0.53.0 removed them from Flow too (the v0.53.0 entry at CHANGELOG.md:77 says "removed from TypeScript and Flow"), and the pending changeset from #9529 brings them back in the next release. Suggested: "Removal: v0.53.0 removed them from TypeScript and Flow (#9479); #9529 restores them, still deprecated, in the Flow declarations of the next release."
  2. CHANGELOG.md:1145-1149. TS2740 is not specific to return values. Any ...OrThrow() result that goes to a subclass-typed variable, property or return value reports it. A nullable return reports TS2322, and getParent reports Type 'ElementNode | null' .... All three codes are listed, so only the "for a return value" attribution is off. The .changeset/README.md example ("a TS2322/TS2740 error for the assignment") already puts it well.
  3. CHANGELOG.md:992-995 and .changeset/README.md:97-100. The new "Type signature changes" section counts widening a return type as breaking when existing code can stop type checking. ListItemNode.collapseAtStart going from true to boolean does that: an override declared : true that returns super.collapseAtStart(selection), or const handled: true = item.collapseAtStart(selection), fails with TS2322 from v0.48.0. Either name that shape in the entry or narrow the README wording so the two agree.
  4. scripts/changesets/entry.mjs:110. The pattern warns on prose that is not about types: invariant (as in Lexical's invariant() helper), "now returns", "generic error message". It stays quiet for "narrow the parameter type" and "make the parameter required", which the README lists as breaking. Dropping bare invariant (the #8877 wording is still caught by generics) and adding param(?:eter)? types? would line it up with the README. A quiet case in scripts/__tests__/unit/changesets.test.ts would pin the result.

Leaving the Flow collapseAtStart declaration to its own change, as the description says, makes sense.

Comment thread CHANGELOG.md
Removal: not scheduled.
Detect: explicit type arguments on these methods, such as `getParentOrThrow<ListItemNode>()`. Calls without one are covered by the Breaking changes entry.

Removal: v0.53.0 for TypeScript; [#9479](https://github.com/meta/lexical/pull/9479) removes the generic overloads. The Flow declarations keep them, still deprecated, after [#9529](https://github.com/meta/lexical/pull/9529).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Optional: v0.53.0 removed these overloads from Flow too (the v0.53.0 entry at line 77 says "removed from TypeScript and Flow"), and #9529 brings them back in the next release, so "keep them" reads as if Flow never lost them. Suggested: "Removal: v0.53.0 removed them from TypeScript and Flow (#9479); #9529 restores them, still deprecated, in the Flow declarations of the next release."

Comment thread CHANGELOG.md
Comment on lines +1145 to +1149
TypeScript reports TS2322 (`Type 'LexicalNode | null' is not assignable to
type ...` or `Type 'LexicalNode[]' is not assignable to type ...`), TS2345
(`Argument of type 'LexicalNode' is not assignable to parameter of type
...`), or, for a return value, TS2740 (`Type 'LexicalNode' is missing the
following properties from type ...`). Calls that pass a type argument (`getFirstChild<ParagraphNode>()`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Optional: TS2740 is not specific to return values. Any ...OrThrow() result that goes to a subclass-typed variable, property or return reports it. A nullable return reports TS2322, and getParent reports Type 'ElementNode | null' .... Suggested: "... or TS2740 (Type 'LexicalNode' is missing the following properties from type ...) when an OrThrow result goes to a subclass type."

Comment thread CHANGELOG.md
Comment on lines +992 to +995
`ListItemNode.collapseAtStart` changed to match and now returns `boolean`
instead of `true`. That widened return type is filed here rather than as
breaking because callers only test the result, and overrides that return
`true` still type check.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Optional: under the new "Type signature changes" rule in .changeset/README.md, this widening can break compiling code: an override declared : true that returns super.collapseAtStart(selection), or const handled: true = item.collapseAtStart(selection), fails with TS2322 from v0.48.0. Consider naming that shape here, or narrowing the README wording so the two agree.

// Wording that suggests a change to a published type signature, which is
// breaking when existing call sites can stop compiling.
const TYPE_CHANGE_RE =
/\b(?:type[ -](?:param(?:eter)?s?|arguments?)|overloads?|generics?|branded|invariant|(?:return|payload) types?|narrow(?:ed|s)? (?:the )?(?:return|payload)|now returns|signatures?)\b/i;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Optional: invariant also matches Lexical's own invariant() helper, and now returns matches ordinary behavior wording, so a chore such as "Replace thrown errors in $splitNode with invariant" is told it may be [breaking]. Meanwhile "Narrow the parameter type of ..." (on the README's breaking list) passes quietly. Dropping bare invariant (the #8877 wording is still caught by generics) and adding param(?:eter)? types? would match the README more closely.

@zurfyx
zurfyx added this pull request to the merge queue Nov 4, 2026
Merged via the queue into main with commit a8e16c9 Nov 4, 2026
73 checks passed

This branch was successfully deployed

2 active deployments
Preview – lexical — 01be899a Deployed Nov 4, 2026 by vercel[bot]
Preview – lexical-playground — 01be899a Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants