Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve typing of TreeBeta.clone() #23553

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

noencke
Copy link
Contributor

@noencke noencke commented Jan 14, 2025

Description

TreeBeta.clone() does not currently infer its output parameter from its input parameter.
This means that callers must explicitly provide the generic parameter in order to get strong typing of the cloned object.
This PR updates the generic typing to infer the output type automatically.

This may be a breaking change for callers who explicitly provided the generic parameter and must now delete it, as noted in the changeset.

@Copilot Copilot bot review requested due to automatic review settings January 14, 2025 22:57
@noencke noencke requested review from a team as code owners January 14, 2025 22:57
@github-actions github-actions bot added base: main PRs targeted against main branch area: dds Issues related to distributed data structures area: dds: tree area: framework Framework is a tag for issues involving the developer framework. Eg Aqueduct changeset-present public api change Changes to a public API labels Jan 14, 2025

Choose a reason for hiding this comment

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

Copilot reviewed 5 out of 8 changed files in this pull request and generated no comments.

Files not reviewed (3)
  • packages/framework/fluid-framework/api-report/fluid-framework.beta.api.md: Evaluated as low risk
  • packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md: Evaluated as low risk
  • packages/dds/tree/api-report/tree.beta.api.md: Evaluated as low risk
Improved typing of `TreeBeta.clone`

The return type of `TreeBeta.clone` now matches the input type.
Existing usages which were supplying the generic parameter for the input/output type should remove the explicit generic parameter and allow it to be inferred from the input.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

FWIW, we could make this a non-breaking change by keeping the old signature around as a second overload. However, since this is a beta API, it seems like it'd be best to just have any consumers be forced to fix it since it's so easy to fix and makes their code immediately nicer.

@noencke noencke requested a review from agarwal-navin January 14, 2025 23:02
assert.equal(clonedMetadata, topLeftPoint.metadata, "String not cloned properly");
});

// Compile-time test: ensure that the return type of clone is the same as the input type for common cases (nodes, primitives, undefined).
function preservesTypesWhenCloning(
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider adding a test where a type is explicitly specified to validate that is working as expected.

Copy link
Contributor

🔗 No broken links found! ✅

Your attention to detail is admirable.

linkcheck output


> [email protected] ci:check-links /home/runner/work/FluidFramework/FluidFramework/docs
> start-server-and-test "npm run serve -- --no-open" 3000 check-links

1: starting server using command "npm run serve -- --no-open"
and when url "[ 'http://127.0.0.1:3000' ]" is responding with HTTP status code 200
running tests using command "npm run check-links"


> [email protected] serve
> docusaurus serve --no-open

[SUCCESS] Serving "build" directory at: http://localhost:3000/

> [email protected] check-links
> linkcheck http://localhost:3000 --skip-file skipped-urls.txt

Crawling...

Stats:
  170508 links
    1603 destination URLs
    1842 URLs ignored
       0 warnings
       0 errors


@@ -808,7 +808,7 @@ export interface TreeArrayNodeUnsafe<TAllowedTypes extends Unenforced<ImplicitAl
// @beta @sealed
export const TreeBeta: {
on<K extends keyof TreeChangeEventsBeta<TNode>, TNode extends TreeNode>(node: TNode, eventName: K, listener: NoInfer<TreeChangeEventsBeta<TNode>[K]>): () => void;
clone<const TSchema extends ImplicitFieldSchema>(node: TreeFieldFromImplicitField<TSchema>): TreeFieldFromImplicitField<TSchema>;
clone<const TNode extends TreeFieldFromImplicitField<ImplicitFieldSchema>>(node: TNode): Unhydrated<TNode>;
Copy link
Contributor

Choose a reason for hiding this comment

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

This introduces a type unsoundness. We still might want to do it, but here is the reasoning why it was not done as you are suggesting in the first place:

As you have written it, it preserves all type information about the node being cloned. This could include information which is not part of the cloned data, and thus produce output which has in incorrect type,

For example consider a node which has a local optional function property "foo" that defaults to undefined.

If you do:

if (n.foo) {
const x = Tree.clone(n);
const result= x.foo(); // This compiles, but x.foo is undefined at runtime since the above type guard narrowed the type of n passed to clone.
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is that actually true? If n is e.g. a class-based schema node instance (which I think is what it would be in your example), then I don't think TS will capture n.foo's defined-ness in the type of n. It merely knows that n.foo is defined. So after the clone, it doesn't extrapolate to x.foo being undefined. Even if the generic parameter is const. See here - let me know if/what I'm missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: dds: tree area: dds Issues related to distributed data structures area: framework Framework is a tag for issues involving the developer framework. Eg Aqueduct base: main PRs targeted against main branch changeset-present public api change Changes to a public API
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants