Skip to content

guarantee that return of TreeDataProvider.getChildren() is not mutated by vscode - #306955

Merged
Alex Ross (alexr00) merged 2 commits into
microsoft:mainfrom
n-gist:treeDataProvider-getChildren-allow-readonlyTArray
May 7, 2026
Merged

Alex Ross (alexr00) merged 2 commits into
microsoft:mainfrom
n-gist:treeDataProvider-getChildren-allow-readonlyTArray

Conversation

@n-gist

Copy link
Copy Markdown
Contributor

Alex Ross (@alexr00), this is corrected version of #306081

Sorry, I didn’t take into account that implementations may call getChildren themselves. This makes it impossible to accept readonly T[] in the public API without breaking changes.

However, some work had already been done, so I converted this from a restriction into a guarantee, which seems like the right direction.

  • Added readonly internally
  • Strengthened related types to ensure no mutations
  • No runtime changes, types only
  • Added a note to the public API guaranteeing no mutations

@vs-code-engineering

Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

Benjamin Pasero (@bpasero)

Matched files:

  • src/vs/workbench/browser/parts/views/treeView.ts
  • src/vs/workbench/common/views.ts

/**
* Get the children of `element` or root if no element is passed.
*
* *Note:* The result is not mutated by the API consumer; readonly arrays may be cast to `T[]`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is more information than most extension developers will want in the API doc. Instead, could we do something like this?

		getChildren(element?: T): ProviderResult<T[] | readonly T[]>;

@n-gist n-gist (n-gist) Apr 1, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That was the original idea, and I would prefer it to be that way.

However, there may be extensions that call getChildren internally and mutate the returned array. While this isn’t the intended usage of TreeDataProvider, it could still exist in practice. If we’re comfortable with those extensions needing to adjust their logic or add casts back to T[] — since TypeScript will surface errors the next time they try to publish — then yes, that works. I assumed this is what you meant by breaking existing extensions.

I suspect such cases are rare (if any), but that’s the only reason I kept ProviderResult<T[]> and added a note instead.

@n-gist n-gist (n-gist) Apr 1, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We probably don’t need T[] | readonly T[] in this case? Since readonly dominates, it’s effectively equivalent to:

getChildren(element?: T): ProviderResult<readonly T[]>

@n-gist n-gist (n-gist) Apr 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

After thinking, actually, such implementations which already include return type definition should not suffer, like:

children: Item[];

getChildren(element?: Item): ProviderResult<Item[]> {
    return children;
}

someMethod() {
    this.getChildren().push(newChildren);
}

This is usually done by autocompletion, and after updating interface to getChildren(element?: T): ProviderResult<readonly T[]> this should be ok, since this definition is assignable to it

Here is the only case where typescript will yell at:

children: Item[];

getChildren(element?: Item) { // will need to specify return type here as ProviderResult<Item[]>
    return children;
}

someMethod() {
    this.getChildren().push(newChild); // or add cast here to Item[]
                    // ^^^^ here will be ts error
}

I think, internal usage of this.getChildren() is already rare, and the cases where return type is not specified + output is mutated is close to, or is zero

@n-gist n-gist (n-gist) May 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This probably should be affected too, along with getChildren
onDidChangeTreeData?: Event<T | T[] | undefined | null | void>;
to
onDidChangeTreeData?: Event<T | readonly T[] | undefined | null | void>;

Let me know if this is going to be accepted, I'll update PR then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To keep the possible breakage at zero, let's keep it as you currently have it in the PR.

/**
* Get the children of `element` or root if no element is passed.
*
* *Note:* The result is not mutated by the API consumer; readonly arrays may be cast to `T[]`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To keep the possible breakage at zero, let's keep it as you currently have it in the PR.

@alexr00
Alex Ross (alexr00) enabled auto-merge (squash) May 7, 2026 09:35
Copilot AI review requested due to automatic review settings May 7, 2026 09:36

Copilot AI left a comment

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.

Pull request overview

This PR updates VS Code’s tree view plumbing to guarantee (and document) that arrays returned from TreeDataProvider.getChildren() are not mutated by VS Code, enabling safe use of immutable/readonly child arrays (via casting) without introducing a breaking API change.

Changes:

  • Documented the non-mutation guarantee in the public vscode.d.ts API docs for TreeDataProvider.getChildren.
  • Strengthened internal workbench and extension-host tree view types to use readonly children arrays throughout the getChildren / batch pipeline.
  • Updated impacted internal call sites and tests to reflect the readonly-aware types.
Show a summary per file
File Description
src/vscode-dts/vscode.d.ts Adds a public API note describing the non-mutation guarantee for getChildren() results.
src/vs/workbench/contrib/userDataSync/browser/userDataSyncConflictsView.ts Adjusts internal tree view data provider typing to return readonly children arrays.
src/vs/workbench/common/views.ts Updates internal tree item/provider contracts to use readonly children arrays.
src/vs/workbench/browser/parts/views/treeView.ts Propagates readonly children array types through the workbench TreeView data provider and batching logic.
src/vs/workbench/api/test/browser/extHostTreeViews.test.ts Updates test helper typing to match readonly wire format.
src/vs/workbench/api/common/extHostTreeViews.ts Updates ext host RPC shape typing for batched children payloads to readonly arrays.
src/vs/workbench/api/common/extHost.protocol.ts Updates the protocol signature for $getChildren to readonly arrays.
src/vs/workbench/api/browser/mainThreadTreeViews.ts Updates main thread tree view provider conversion/post-processing typings to readonly arrays.

Copilot's findings

  • Files reviewed: 7/8 changed files
  • Comments generated: 2

@alexr00
Alex Ross (alexr00) merged commit d327686 into microsoft:main May 7, 2026
30 checks passed
@vs-code-engineering vs-code-engineering Bot added this to the 1.120.0 milestone May 7, 2026
@vs-code-engineering vs-code-engineering Bot locked and limited conversation to collaborators Jun 24, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants