Skip to content

Commit

Permalink
Update LiveMap.get to always return undefined value as an option …
Browse files Browse the repository at this point in the history
…for keys that reference other LiveObjects

Referenced live objects can be not valid (haven't seen a create op) and
we should not surface such objects to the end user and return `undefined`
instead.
  • Loading branch information
VeskeR committed Dec 6, 2024
1 parent 449ab52 commit 8005fd2
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 7 deletions.
6 changes: 3 additions & 3 deletions ably.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2103,12 +2103,12 @@ export type DefaultRoot =
*/
export declare interface LiveMap<T extends LiveMapType> extends LiveObject<LiveMapUpdate> {
/**
* Returns the value associated with a given key. Returns `undefined` if the key doesn't exist in a map.
* Returns the value associated with a given key. Returns `undefined` if the key doesn't exist in a map or if the associated {@link LiveObject} has been deleted.
*
* @param key - The key to retrieve the value for.
* @returns A {@link LiveObject}, a primitive type (string, number, boolean, or binary data) or `undefined` if the key doesn't exist in a map.
* @returns A {@link LiveObject}, a primitive type (string, number, boolean, or binary data) or `undefined` if the key doesn't exist in a map or the associated {@link LiveObject} has been deleted.
*/
get<TKey extends keyof T & string>(key: TKey): T[TKey];
get<TKey extends keyof T & string>(key: TKey): T[TKey] extends StateValue ? T[TKey] : T[TKey] | undefined;

/**
* Returns the number of key/value pairs in the map.
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/liveobjects/livemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
* - If the value is not an objectId, then that value is returned.
*/
// force the key to be of type string as we only allow strings as key in a map
get<TKey extends keyof T & string>(key: TKey): T[TKey] {
get<TKey extends keyof T & string>(key: TKey): T[TKey] extends StateValue ? T[TKey] : T[TKey] | undefined {
const element = this._dataRef.data.get(key);

if (element === undefined) {
Expand Down
4 changes: 2 additions & 2 deletions test/package/browser/template/src/ably.config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ type CustomRoot = {
stringKey: string;
booleanKey: boolean;
couldBeUndefined?: string;
mapKey?: LiveMap<{
mapKey: LiveMap<{
foo: 'bar';
nestedMap?: LiveMap<{
baz: 'qux';
}>;
}>;
counterKey?: LiveCounter;
counterKey: LiveCounter;
};

declare global {
Expand Down
4 changes: 3 additions & 1 deletion test/package/browser/template/src/index-liveobjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ globalThis.testAblyPackage = async function () {
const aBoolean: boolean = root.get('booleanKey');
const couldBeUndefined: string | undefined = root.get('couldBeUndefined');
// live objects on a root:
// LiveMap.get can still return undefined for LiveObject typed properties even if custom typings have them as non-optional.
// objects can be tombstoned/non-valid and result in the undefined value
const counter: Ably.LiveCounter | undefined = root.get('counterKey');
const map: LiveObjectsTypes['root']['mapKey'] = root.get('mapKey');
const map: LiveObjectsTypes['root']['mapKey'] | undefined = root.get('mapKey');
// check string literal types works
// need to use nullish coalescing as we didn't actually create any data on the root,
// so the next calls would fail. we only need to check that TypeScript types work
Expand Down

0 comments on commit 8005fd2

Please sign in to comment.