]> Lady’s Gitweb - Pisces/blob - object.js
toPrimitive and toPropertyKey
[Pisces] / object.js
1 // ♓🌟 Piscēs ∷ object.js
2 // ====================================================================
3 //
4 // Copyright © 2022 Lady [@ Lady’s Computer].
5 //
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
9
10 /** Returns whether the provided value is a constructor. */
11 export const isConstructor = ($) => {
12 if (!isObject($)) {
13 // The provided value is not an object.
14 return false;
15 } else {
16 // The provided value is an object.
17 try {
18 Reflect.construct(
19 function () {},
20 [],
21 $,
22 ); // will throw if $ is not a constructor
23 return true;
24 } catch {
25 return false;
26 }
27 }
28 };
29
30 /** Returns whether the provided value is an object. */
31 export const isObject = ($) => {
32 return $ !== null &&
33 (typeof $ == "function" || typeof $ == "object");
34 };
35
36 /**
37 * Returns whether the provided object inherits from the prototype of
38 * the provided function.
39 */
40 export const ordinaryHasInstance = Function.prototype.call.bind(
41 Function.prototype[Symbol.hasInstance],
42 );
43
44 /**
45 * Returns the primitive value of the provided object per its
46 * `toString` and `valueOf` methods.
47 *
48 * If the provided hint is "string", then `toString` takes precedence;
49 * otherwise, `valueOf` does.
50 *
51 * Throws an error if both of these methods are not callable or do not
52 * return a primitive.
53 */
54 export const ordinaryToPrimitive = (O, hint) => {
55 for (
56 const name of hint == "string"
57 ? ["toString", "valueOf"]
58 : ["valueOf", "toString"]
59 ) {
60 const method = O[name];
61 if (typeof method == "function") {
62 // Method is callable.
63 const result = method.call(O);
64 if (!isObject(result)) {
65 // Method returns a primitive.
66 return result;
67 } else {
68 // Method returns an object.
69 continue;
70 }
71 } else {
72 // Method is not callable.
73 continue;
74 }
75 }
76 throw new TypeError("Piscēs: Unable to convert object to primitive");
77 };
78
79 /**
80 * Returns the provided value converted to a primitive, or throws if
81 * no such conversion is possible.
82 *
83 * The provided preferred type, if specified, should be "string",
84 * "number", or "default". If the provided input has a
85 * `Symbol.toPrimitive` method, this function will throw rather than
86 * calling that method with a preferred type other than one of the
87 * above.
88 */
89 export const toPrimitive = ($, preferredType) => {
90 if (isObject($)) {
91 // The provided value is an object.
92 const exoticToPrim = $[Symbol.toPrimitive] ?? undefined;
93 if (exoticToPrim !== undefined) {
94 // The provided value has an exotic primitive conversion method.
95 if (typeof exoticToPrim != "function") {
96 // The method is not callable.
97 throw new TypeError(
98 "Piscēs: Symbol.toPrimitive was neither nullish nor callable.",
99 );
100 } else {
101 // The method is callable.
102 const hint = `${preferredType ?? "default"}`;
103 if (!["default", "string", "number"].includes(hint)) {
104 // An invalid preferred type was specified.
105 throw new TypeError(
106 `Piscēs: Invalid preferred type: ${preferredType}.`,
107 );
108 } else {
109 // The resulting hint is either default, string, or number.
110 return exoticToPrim.call($, hint);
111 }
112 }
113 } else {
114 // Use the ordinary primitive conversion function.
115 ordinaryToPrimitive($, hint);
116 }
117 } else {
118 // The provided value is already a primitive.
119 return $;
120 }
121 };
122
123 /**
124 * Returns the property key (symbol or string) corresponding to the
125 * provided value.
126 */
127 export const toPropertyKey = ($) => {
128 const key = toPrimitive($, "string");
129 return typeof key == "symbol" ? key : `${key}`;
130 };
This page took 0.067992 seconds and 5 git commands to generate.