Improve types for get(), set(), and MapType
This commit is contained in:
		
							parent
							
								
									5db1eed181
								
							
						
					
					
						commit
						e9a8af86e5
					
				@ -21,12 +21,25 @@ import * as iterator from 'lib0/iterator'
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @template T
 | 
			
		||||
 * @typedef {Extract<keyof T, string>} StringKey<T>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This works around some weird JSDoc+TS circular reference issues: https://github.com/microsoft/TypeScript/issues/46369
 | 
			
		||||
 * @typedef {boolean|null|string|number|Uint8Array|JsonArray|JsonObject} Json
 | 
			
		||||
 * @typedef {Json[]} JsonArray
 | 
			
		||||
 * @typedef {{ [key: string]: Json }} JsonObject
 | 
			
		||||
 * @typedef {Json|AbstractType<any>} MapValue
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @template {Record<string, MapValue>} T
 | 
			
		||||
 * @extends YEvent<YMap<T>>
 | 
			
		||||
 * Event that describes the changes on a YMap.
 | 
			
		||||
 */
 | 
			
		||||
export class YMapEvent extends YEvent {
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {YMap<T>} ymap The YArray that changed.
 | 
			
		||||
   * @param {YMap<T>} ymap The YMap that changed.
 | 
			
		||||
   * @param {Transaction} transaction
 | 
			
		||||
   * @param {Set<any>} subs The keys that changed.
 | 
			
		||||
   */
 | 
			
		||||
@ -37,7 +50,7 @@ export class YMapEvent extends YEvent {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @template MapType
 | 
			
		||||
 * @template {Record<string, MapValue>} MapType
 | 
			
		||||
 * A shared Map implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * @extends AbstractType<YMapEvent<MapType>>
 | 
			
		||||
@ -76,7 +89,8 @@ export class YMap extends AbstractType {
 | 
			
		||||
  _integrate (y, item) {
 | 
			
		||||
    super._integrate(y, item)
 | 
			
		||||
    ;/** @type {Map<string, any>} */ (this._prelimContent).forEach((value, key) => {
 | 
			
		||||
      this.set(key, value)
 | 
			
		||||
      // TODO: fix
 | 
			
		||||
      this.set(/** @type {any} */ (key), value)
 | 
			
		||||
    })
 | 
			
		||||
    this._prelimContent = null
 | 
			
		||||
  }
 | 
			
		||||
@ -97,7 +111,8 @@ export class YMap extends AbstractType {
 | 
			
		||||
     */
 | 
			
		||||
    const map = new YMap()
 | 
			
		||||
    this.forEach((value, key) => {
 | 
			
		||||
      map.set(key, value instanceof AbstractType ? /** @type {typeof value} */ (value.clone()) : value)
 | 
			
		||||
      // TODO: fix
 | 
			
		||||
      map.set(key, /** @type {any} */ (value instanceof AbstractType ? /** @type {typeof value} */ (value.clone()) : value))
 | 
			
		||||
    })
 | 
			
		||||
    return map
 | 
			
		||||
  }
 | 
			
		||||
@ -170,12 +185,12 @@ export class YMap extends AbstractType {
 | 
			
		||||
  /**
 | 
			
		||||
   * Executes a provided function on once on every key-value pair.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {function(MapType,string,YMap<MapType>):void} f A function to execute on every element of this YArray.
 | 
			
		||||
   * @param {function(MapType[keyof MapType],StringKey<MapType>,YMap<MapType>):void} f A function to execute on every element of this YArray.
 | 
			
		||||
   */
 | 
			
		||||
  forEach (f) {
 | 
			
		||||
    this._map.forEach((item, key) => {
 | 
			
		||||
      if (!item.deleted) {
 | 
			
		||||
        f(item.content.getContent()[item.length - 1], key, this)
 | 
			
		||||
        f(item.content.getContent()[item.length - 1], /** @type {StringKey<MapType>} */ (key), this)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
@ -192,6 +207,7 @@ export class YMap extends AbstractType {
 | 
			
		||||
  /**
 | 
			
		||||
   * Remove a specified element from this YMap.
 | 
			
		||||
   *
 | 
			
		||||
   * TODO: type to only allow deletion of optional elements in MapType
 | 
			
		||||
   * @param {string} key The key of the element to remove.
 | 
			
		||||
   */
 | 
			
		||||
  delete (key) {
 | 
			
		||||
@ -205,17 +221,18 @@ export class YMap extends AbstractType {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @template {StringKey<MapType>} Key
 | 
			
		||||
   * @template {MapType[Key]} Value
 | 
			
		||||
   * Adds or updates an element with a specified key and value.
 | 
			
		||||
   * @template {MapType} VAL
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} key The key of the element to add to this YMap
 | 
			
		||||
   * @param {VAL} value The value of the element to add
 | 
			
		||||
   * @return {VAL}
 | 
			
		||||
   * @param {Key} key The key of the element to add to this YMap
 | 
			
		||||
   * @param {Value} value The value of the element to add
 | 
			
		||||
   * @return {Value}
 | 
			
		||||
   */
 | 
			
		||||
  set (key, value) {
 | 
			
		||||
    if (this.doc !== null) {
 | 
			
		||||
      transact(this.doc, transaction => {
 | 
			
		||||
        typeMapSet(transaction, this, key, /** @type {any} */ (value))
 | 
			
		||||
        typeMapSet(transaction, this, key, value)
 | 
			
		||||
      })
 | 
			
		||||
    } else {
 | 
			
		||||
      /** @type {Map<string, any>} */ (this._prelimContent).set(key, value)
 | 
			
		||||
@ -224,13 +241,14 @@ export class YMap extends AbstractType {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @template {StringKey<MapType>} Key
 | 
			
		||||
   * Returns a specified element from this YMap.
 | 
			
		||||
   *
 | 
			
		||||
   * @param {string} key
 | 
			
		||||
   * @return {MapType|undefined}
 | 
			
		||||
   * @param {Key} key
 | 
			
		||||
   * @return {MapType[Key]|undefined}
 | 
			
		||||
   */
 | 
			
		||||
  get (key) {
 | 
			
		||||
    return /** @type {any} */ (typeMapGet(this, key))
 | 
			
		||||
    return /** @type {MapType[Key]|undefined} */ (typeMapGet(this, key))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@ -273,3 +291,9 @@ export class YMap extends AbstractType {
 | 
			
		||||
 * @function
 | 
			
		||||
 */
 | 
			
		||||
export const readYMap = _decoder => new YMap()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** @type {YMap<{ foo: { hello: [3, 4, { hiThere: null }] }; }>} */
 | 
			
		||||
const foo = new YMap();
 | 
			
		||||
 | 
			
		||||
foo.has("")
 | 
			
		||||
 | 
			
		||||
@ -329,6 +329,7 @@ export const testUndoUntilChangePerformed = _tc => {
 | 
			
		||||
  const yMap = new Y.Map()
 | 
			
		||||
  yMap.set('hello', 'world')
 | 
			
		||||
  yArray.push([yMap])
 | 
			
		||||
  /** @type {Y.Map<{ key: string }>} */
 | 
			
		||||
  const yMap2 = new Y.Map()
 | 
			
		||||
  yMap2.set('key', 'value')
 | 
			
		||||
  yArray.push([yMap2])
 | 
			
		||||
@ -342,7 +343,7 @@ export const testUndoUntilChangePerformed = _tc => {
 | 
			
		||||
  Y.transact(doc2, () => yArray2.delete(0), doc2.clientID)
 | 
			
		||||
  undoManager2.undo()
 | 
			
		||||
  undoManager.undo()
 | 
			
		||||
  t.compareStrings(yMap2.get('key'), 'value')
 | 
			
		||||
  t.compareStrings(yMap2.get('key') || '', 'value')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@ -19,14 +19,16 @@ export const testMapHavingIterableAsConstructorParamTests = tc => {
 | 
			
		||||
  t.assert(m1.get('number') === 1)
 | 
			
		||||
  t.assert(m1.get('string') === 'hello')
 | 
			
		||||
 | 
			
		||||
  /** @type {Y.Map<{ object: { x: number; }; boolean: boolean; }>} */
 | 
			
		||||
  const m2 = new Y.Map([
 | 
			
		||||
    ['object', { x: 1 }],
 | 
			
		||||
    ['boolean', true]
 | 
			
		||||
  ])
 | 
			
		||||
  map0.set('m2', m2)
 | 
			
		||||
  t.assert(m2.get('object').x === 1)
 | 
			
		||||
  t.assert(m2.get('object')?.x === 1)
 | 
			
		||||
  t.assert(m2.get('boolean') === true)
 | 
			
		||||
 | 
			
		||||
  /** @type {Y.Map<any>} */
 | 
			
		||||
  const m3 = new Y.Map([...m1, ...m2])
 | 
			
		||||
  map0.set('m3', m3)
 | 
			
		||||
  t.assert(m3.get('number') === 1)
 | 
			
		||||
@ -281,7 +283,7 @@ export const testGetAndSetAndDeleteOfMapPropertyWithThreeConflicts = tc => {
 | 
			
		||||
 */
 | 
			
		||||
export const testObserveDeepProperties = tc => {
 | 
			
		||||
  const { testConnector, users, map1, map2, map3 } = init(tc, { users: 4 })
 | 
			
		||||
  const _map1 = map1.set('map', new Y.Map())
 | 
			
		||||
  const _map1 = map1.set('map', /** @type {Y.Map<{ deepmap: Y.Map<any> }>} */ (new Y.Map()))
 | 
			
		||||
  let calls = 0
 | 
			
		||||
  let dmapid
 | 
			
		||||
  map1.observeDeep(events => {
 | 
			
		||||
@ -306,10 +308,10 @@ export const testObserveDeepProperties = tc => {
 | 
			
		||||
  const dmap2 = _map2.get('deepmap')
 | 
			
		||||
  const dmap3 = _map3.get('deepmap')
 | 
			
		||||
  t.assert(calls > 0)
 | 
			
		||||
  t.assert(compareIDs(dmap1._item.id, dmap2._item.id))
 | 
			
		||||
  t.assert(compareIDs(dmap1._item.id, dmap3._item.id))
 | 
			
		||||
  t.assert(compareIDs(dmap1?._item?.id || null, dmap2._item.id))
 | 
			
		||||
  t.assert(compareIDs(dmap1?._item?.id || null, dmap3._item.id))
 | 
			
		||||
  // @ts-ignore we want the possibility of dmapid being undefined
 | 
			
		||||
  t.assert(compareIDs(dmap1._item.id, dmapid))
 | 
			
		||||
  t.assert(compareIDs(dmap1?._item?.id || null, dmapid))
 | 
			
		||||
  compare(users)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user