function recurse(slice, opts) {
  // we use a Set to avoid duplicate values
  /** @type {Set<string>} */
  let types = new Set();

  // this slice within the dictionary is a design token
  // Check for $value in case the input is already DTCG syntax
  if (Object.hasOwn(slice, 'value') || Object.hasOwn(slice, '$value')) {
    const token = /** @type {DesignToken} */ (slice);
    Object.keys(token).forEach((key) => {
      if (['type', '$type'].includes(key)) {
        // track the encountered types for this layer
        types.add(/** @type {string} */ (token[key]));
      }
      // convert to $ prefixed properties
      if (['type', 'description', 'value'].includes(key)) {
        token[`$${key}`] = token[key];
        delete token[key];
      }
    });
    return types;
  } else {
    // token group, not a token
    // go through all props and call itself recursively for object-value props
    Object.keys(slice).forEach((key) => {
      const prop = slice[key];
      if (isPlainObject(prop)) {
        // call Set again to dedupe the accumulation of the two sets
        types = new Set([...types, ...recurse(prop, opts)]);
      }
    });

    // Now that we've checked every property, let's see how many types we found
    // If it's only 1 type, we know we can apply the type on the ancestor group
    // and remove it from the children
    if (types.size === 1 && opts?.applyTypesToGroup !== false) {
      const groupType = [...types][0];
      const entries = Object.entries(slice).map(([key, value]) => {
        if (isPlainObject(value)) {
          // remove the type from the child
          delete value.$type;
        }
        return /** @type {[string, DesignToken|DesignTokens]} */ ([key, value]);
      });

      Object.keys(slice).forEach((key) => {
        delete slice[key];
      });
      // put the type FIRST
      slice.$type = groupType;
      // then put the rest of the key value pairs back, now we always ordered $type first on the token group
      entries.forEach(([key, value]) => {
        if (key !== '$type') {
          slice[key] = value;
        }
      });
    }
  }
  return types;
}
@param {DesignTokens} slice
@param {{applyTypesToGroup?: boolean}} [opts]