diff --git a/src/types/YMap.js b/src/types/YMap.js index c640ae54..e2dd7a49 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -206,9 +206,11 @@ export class YMap extends AbstractType { /** * 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 {MapType} value The value of the element to add + * @param {VAL} value The value of the element to add + * @return {VAL} */ set (key, value) { if (this.doc !== null) { diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 905ad2a3..92088cdd 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -1,3 +1,4 @@ +import * as object from 'lib0/object' import { YXmlFragment, @@ -12,6 +13,10 @@ import { YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item // eslint-disable-line } from '../internals.js' +/** + * @typedef {Object|number|null|Array|string|Uint8Array|AbstractType} ValueTypes + */ + /** * An YXmlElement imitates the behavior of a * {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}. @@ -19,7 +24,7 @@ import { * * An YXmlElement has attributes (key value pairs) * * An YXmlElement has childElements that must inherit from YXmlElement * - * @template {{ [key: string]: Object|number|null|Array|string|Uint8Array|AbstractType }} [KV={ [key: string]: string }] + * @template {{ [key: string]: ValueTypes }} [KV={ [key: string]: string }] */ export class YXmlElement extends YXmlFragment { constructor (nodeName = 'UNDEFINED') { @@ -75,14 +80,19 @@ export class YXmlElement extends YXmlFragment { } /** - * @return {YXmlElement} + * @return {YXmlElement} */ clone () { + /** + * @type {YXmlElement} + */ const el = new YXmlElement(this.nodeName) const attrs = this.getAttributes() - for (const key in attrs) { - el.setAttribute(key, attrs[key]) - } + object.forEach(attrs, (value, key) => { + if (typeof value === 'string') { + el.setAttribute(key, value) + } + }) // @ts-ignore el.insert(0, this.toArray().map(item => item instanceof AbstractType ? item.clone() : item)) return el @@ -182,12 +192,12 @@ export class YXmlElement extends YXmlFragment { /** * Returns all attribute name/value pairs in a JSON Object. * - * @return {Object} A JSON Object that describes the attributes. + * @return {{ [Key in Extract]?: KV[Key]}} A JSON Object that describes the attributes. * * @public */ getAttributes () { - return typeMapGetAll(this) + return /** @type {any} */ (typeMapGetAll(this)) } /** @@ -209,7 +219,10 @@ export class YXmlElement extends YXmlFragment { const dom = _document.createElement(this.nodeName) const attrs = this.getAttributes() for (const key in attrs) { - dom.setAttribute(key, attrs[key]) + const value = attrs[key] + if (typeof value === 'string') { + dom.setAttribute(key, value) + } } typeListForEach(this, yxml => { dom.appendChild(yxml.toDOM(_document, hooks, binding)) diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 619d9ee9..1ae2c813 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -3,6 +3,33 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' +export const testCustomTypings = () => { + const ydoc = new Y.Doc() + const ymap = ydoc.getMap() + /** + * @type {Y.XmlElement<{ num: number, str: string, [k:string]: object|number|string }>} + */ + const yxml = ymap.set('yxml', new Y.XmlElement('test')) + /** + * @type {number|undefined} + */ + const num = yxml.getAttribute('num') + /** + * @type {string|undefined} + */ + const str = yxml.getAttribute('str') + /** + * @type {object|number|string|undefined} + */ + const dtrn = yxml.getAttribute('dtrn') + const attrs = yxml.getAttributes() + /** + * @type {object|number|string|undefined} + */ + const any = attrs.shouldBeAny + console.log({ num, str, dtrn, attrs, any }) +} + /** * @param {t.TestCase} tc */ @@ -92,9 +119,9 @@ export const testTreewalker = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testYtextAttributes = tc => { +export const testYtextAttributes = _tc => { const ydoc = new Y.Doc() const ytext = /** @type {Y.XmlText} */ (ydoc.get('', Y.XmlText)) ytext.observe(event => { @@ -106,9 +133,9 @@ export const testYtextAttributes = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testSiblings = tc => { +export const testSiblings = _tc => { const ydoc = new Y.Doc() const yxml = ydoc.getXmlFragment() const first = new Y.XmlText() @@ -122,9 +149,9 @@ export const testSiblings = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testInsertafter = tc => { +export const testInsertafter = _tc => { const ydoc = new Y.Doc() const yxml = ydoc.getXmlFragment() const first = new Y.XmlText() @@ -152,9 +179,9 @@ export const testInsertafter = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testClone = tc => { +export const testClone = _tc => { const ydoc = new Y.Doc() const yxml = ydoc.getXmlFragment() const first = new Y.XmlText('text') @@ -170,9 +197,9 @@ export const testClone = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testFormattingBug = tc => { +export const testFormattingBug = _tc => { const ydoc = new Y.Doc() const yxml = /** @type {Y.XmlText} */ (ydoc.get('', Y.XmlText)) const delta = [