]> Lady’s Gitweb - Pisces/blobdiff - object.js
toPrimitive and toPropertyKey
[Pisces] / object.js
index 502207e3b6ed1f8496300af183e968c9a72e3f9e..d08f43a00283853679c32b8dcff76b77350fad54 100644 (file)
--- a/object.js
+++ b/object.js
@@ -40,3 +40,91 @@ export const isObject = ($) => {
 export const ordinaryHasInstance = Function.prototype.call.bind(
   Function.prototype[Symbol.hasInstance],
 );
+
+/**
+ * Returns the primitive value of the provided object per its
+ * `toString` and `valueOf` methods.
+ *
+ * If the provided hint is "string", then `toString` takes precedence;
+ * otherwise, `valueOf` does.
+ *
+ * Throws an error if both of these methods are not callable or do not
+ * return a primitive.
+ */
+export const ordinaryToPrimitive = (O, hint) => {
+  for (
+    const name of hint == "string"
+      ? ["toString", "valueOf"]
+      : ["valueOf", "toString"]
+  ) {
+    const method = O[name];
+    if (typeof method == "function") {
+      // Method is callable.
+      const result = method.call(O);
+      if (!isObject(result)) {
+        // Method returns a primitive.
+        return result;
+      } else {
+        // Method returns an object.
+        continue;
+      }
+    } else {
+      // Method is not callable.
+      continue;
+    }
+  }
+  throw new TypeError("Piscēs: Unable to convert object to primitive");
+};
+
+/**
+ * Returns the provided value converted to a primitive, or throws if
+ * no such conversion is possible.
+ *
+ * The provided preferred type, if specified, should be "string",
+ * "number", or "default". If the provided input has a
+ * `Symbol.toPrimitive` method, this function will throw rather than
+ * calling that method with a preferred type other than one of the
+ * above.
+ */
+export const toPrimitive = ($, preferredType) => {
+  if (isObject($)) {
+    // The provided value is an object.
+    const exoticToPrim = $[Symbol.toPrimitive] ?? undefined;
+    if (exoticToPrim !== undefined) {
+      // The provided value has an exotic primitive conversion method.
+      if (typeof exoticToPrim != "function") {
+        // The method is not callable.
+        throw new TypeError(
+          "Piscēs: Symbol.toPrimitive was neither nullish nor callable.",
+        );
+      } else {
+        // The method is callable.
+        const hint = `${preferredType ?? "default"}`;
+        if (!["default", "string", "number"].includes(hint)) {
+          // An invalid preferred type was specified.
+          throw new TypeError(
+            `Piscēs: Invalid preferred type: ${preferredType}.`,
+          );
+        } else {
+          // The resulting hint is either default, string, or number.
+          return exoticToPrim.call($, hint);
+        }
+      }
+    } else {
+      // Use the ordinary primitive conversion function.
+      ordinaryToPrimitive($, hint);
+    }
+  } else {
+    // The provided value is already a primitive.
+    return $;
+  }
+};
+
+/**
+ * Returns the property key (symbol or string) corresponding to the
+ * provided value.
+ */
+export const toPropertyKey = ($) => {
+  const key = toPrimitive($, "string");
+  return typeof key == "symbol" ? key : `${key}`;
+};
This page took 0.055568 seconds and 4 git commands to generate.