function unsafeClone(obj) {
  if (!obj || typeof obj !== 'object') return obj;
  if (typeof obj.clone === 'function') return obj.clone();
  if (typeof Map !== 'undefined' && obj instanceof Map) {
    return new Map(obj);
  } else if (typeof Set !== 'undefined' && obj instanceof Set) {
    return new Set(obj);
  } else if (obj instanceof Date) {
    return new Date(obj.getTime());
  }
  const clone = Array.isArray(obj) ? [] :
      typeof ArrayBuffer === 'function' &&
          typeof ArrayBuffer.isView === 'function' && ArrayBuffer.isView(obj) &&
          !(obj instanceof DataView) ?
                                     new obj.constructor(obj.length) :
                                     {};
  for (const key in obj) {
    clone[key] = unsafeClone(obj[key]);
  }
  return clone;
}
Clones a value. The input may be an Object, Array, or basic type. Objects and
arrays will be cloned recursively.
WARNINGS:
<code>unsafeClone</code> does not detect reference loops. Objects
that refer to themselves will cause infinite recursion.
<code>unsafeClone</code> is unaware of unique identifiers, and
copies UIDs created by <code>getUid</code> into cloned results.
@param {T} obj The value to clone.
@return {T} A clone of the input value.
@template T