-
Notifications
You must be signed in to change notification settings - Fork 535
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Expose .is() typeguard on SharedObjectKind (#21850)
## Description With the removal of concrete DDS classes, one pattern that's come up repeatedly is customer code which previously checked `instanceof MySharedObject` (usually when the code supported multiple shared object types for whatever reason), which no longer works. This change adds a drop-in replacement to the public API surface. ## Breaking Changes As `SharedObjectKind` was marked sealed, this is non-breaking. ## Alternatives Considered We could expose free functions in each package easily e.g. by using a helper like this: ```typescript export function createSharedObjectTypeguard<TSharedObject>( kind: ISharedObjectKind<TSharedObject>, ): (loadable: IFluidLoadable) => loadable is IFluidLoadable & TSharedObject { const factoryType = kind.getFactory().type; return (loadable: IFluidLoadable): loadable is IFluidLoadable & TSharedObject => { return isChannel(loadable) && loadable.attributes.type === factoryType; }; } ``` Ultimately this will be more code though and arguably less discoverable. We could also add back support for `instanceof` using `Symbol.hasInstance` (and the same implementation as `.is`), but due to microsoft/TypeScript#56536, this won't work for customers using TS below 5.5, so we'll need something else anyway at least for now. --------- Co-authored-by: Abram Sanderson <[email protected]> Co-authored-by: Craig Macomber (Microsoft) <[email protected]>
- Loading branch information
1 parent
3efdd9e
commit 6bdec1a
Showing
31 changed files
with
200 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
--- | ||
"@fluidframework/shared-object-base": minor | ||
--- | ||
|
||
Added typeguard for SharedObjectKinds | ||
|
||
In the 2.0 release of Fluid, the concrete class implementations for DDSes were hidden from Fluid's API surface. | ||
This made `instanceof` checks fail to work correctly. | ||
There were ways to work around this in application code, but they involved boilerplate which required more understanding of Fluid internals than should be necessary. | ||
This change adds a drop-in replacement to `instanceof`: the `.is()` method to `SharedObjectKind`. | ||
For example: | ||
|
||
```typescript | ||
// Works in FluidFramework 1.0 but not in the initial release of FluidFramework 2.0: | ||
if (myObject instanceof SharedString) { | ||
// do something | ||
} | ||
|
||
// With this change, that code can now be written like so: | ||
if (SharedString.is(myObject)) { | ||
// do something | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
packages/dds/shared-object-base/src/test/createSharedObjectKind.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/*! | ||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
import { strict as assert } from "assert"; | ||
|
||
import type { IFluidLoadable } from "@fluidframework/core-interfaces"; | ||
import type { | ||
IChannel, | ||
IChannelAttributes, | ||
IChannelFactory, | ||
IChannelServices, | ||
IFluidDataStoreRuntime, | ||
} from "@fluidframework/datastore-definitions/internal"; | ||
import { MockFluidDataStoreRuntime } from "@fluidframework/test-runtime-utils/internal"; | ||
|
||
import { createSharedObjectKind } from "../sharedObject.js"; | ||
|
||
interface IFoo { | ||
foo: string; | ||
} | ||
class SharedFooFactory implements IChannelFactory<IFoo> { | ||
public static readonly Type: string = "SharedFoo"; | ||
public readonly type: string = SharedFooFactory.Type; | ||
public readonly attributes: IChannelAttributes = { | ||
type: SharedFooFactory.Type, | ||
snapshotFormatVersion: "0.1", | ||
}; | ||
async load( | ||
runtime: IFluidDataStoreRuntime, | ||
id: string, | ||
services: IChannelServices, | ||
channelAttributes: Readonly<IChannelAttributes>, | ||
): Promise<IFoo & IChannel> { | ||
throw new Error("Method not implemented."); | ||
} | ||
create(runtime: IFluidDataStoreRuntime, id: string): IFoo & IChannel { | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
return { | ||
foo: "bar", | ||
attributes: this.attributes, | ||
id, | ||
// Note: other IChannel methods aren't relevant | ||
} as IFoo & IChannel; | ||
} | ||
} | ||
|
||
const SharedFoo = createSharedObjectKind<IFoo>(SharedFooFactory); | ||
|
||
describe("createSharedObjectKind's return type", () => { | ||
it("delegates to runtime.createChannel on creation", () => { | ||
const createChannelCalls: [id: string | undefined, type: string][] = []; | ||
const runtime = new MockFluidDataStoreRuntime(); | ||
runtime.createChannel = (id: string | undefined, type: string) => { | ||
createChannelCalls.push([id, type]); | ||
return null as unknown as IChannel; | ||
}; | ||
SharedFoo.create(runtime); | ||
assert.deepEqual(createChannelCalls, [[undefined, SharedFooFactory.Type]]); | ||
createChannelCalls.length = 0; | ||
SharedFoo.create(runtime, "test-id"); | ||
assert.deepEqual(createChannelCalls, [["test-id", SharedFooFactory.Type]]); | ||
}); | ||
|
||
describe(".is", () => { | ||
it("returns true for objects created by the factory", () => { | ||
const factory = SharedFoo.getFactory(); | ||
const foo = factory.create(new MockFluidDataStoreRuntime(), "test-id"); | ||
assert(SharedFoo.is(foo)); | ||
}); | ||
describe("returns false for", () => { | ||
const cases: [name: string, obj: unknown][] = [ | ||
["object without attributres", {}], | ||
["object with wrong type", { attributes: { type: "NotSharedFoo" } }], | ||
]; | ||
for (const [name, obj] of cases) { | ||
it(name, () => { | ||
assert(!SharedFoo.is(obj as IFluidLoadable)); | ||
}); | ||
} | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.