Skip to content

Instantly share code, notes, and snippets.

@indifferentghost
Created November 22, 2020 05:42
Show Gist options
  • Save indifferentghost/b2f8daacfd890031e0b2a0ea0f938b22 to your computer and use it in GitHub Desktop.
Save indifferentghost/b2f8daacfd890031e0b2a0ea0f938b22 to your computer and use it in GitHub Desktop.
Implementation of Abstract Equality Comparison
function getMethod(value, property) {
if (typeof property === 'string' || typeof property === 'symbol') {
const func = value[property]
if (func === null || func === undefined) {
return undefined;
}
if (typeof func !== 'function') {
throw new TypeError(`property: ${property} on value is not a function.`)
}
return func;
}
}
function ordinaryToPrimitive(obj, hint) {
if (
typeof obj === 'object' &&
(hint === 'number' || hint === 'string')
) {
const methodNames = hint === 'number'
? ['toString', 'valueOf']
: ['valueOf', 'toString'];
for (let i = 0; i < methodNames.length; i++) {
const method = obj[methodNames[i]]
if (typeof method === 'function') {
const result = Reflect.apply(method, obj, [])
if (typeof result !== 'object') {
return result
}
}
}
}
throw new TypeError('Couldn\'t convert object to primitive value');
}
function toPrimitive(input, preferredType) {
if (typeof input === 'object') {
let exoticToPrim = getMethod(input, Symbol.toPrimitive)
if (exoticToPrim !== undefined) {
const hint = preferredType ?? 'default';
const result = Reflect.apply(exoticToPrim, undefined, [input, hint])
if (typeof result !== 'object') return result;
throw new TypeError('Object couldn\'t be converted to non-object');
}
return ordinaryToPrimitive(input, preferredType || 'number');
}
return input;
}
function maybe(x, y) {
if (typeof x === typeof y) {
return x === y
} else if (x === null && y === undefined) {
return true;
} else if (x === undefined && y === null) {
return true
} else if (typeof x === 'number' && typeof y === 'string') {
return x === Number(y)
} else if (typeof x === 'string' && typeof y === 'number') {
return Number(x) === y
} else if (typeof x === 'bigint' && typeof y === 'string') {
const bigIntY = BigInt(y)
if (Number.isNaN(bigIntY)) return false;
return x === bigIntY;
} else if (typeof x === 'string' && typeof y === 'bigint') {
return maybe(y, x)
} else if (typeof x === 'boolean') {
return maybe(Number(x), y)
} else if (typeof y === 'boolean') {
return maybe(x, Number(y))
} else if (typeof y === 'object') {
if (['string', 'number', 'bigint', 'symbol'].some(val => typeof x === val)) {
return maybe(x, toPrimitive(y))
}
} else if (typeof x === 'object') {
if (['string', 'number', 'bigint', 'symbol'].some(val => typeof y === val)) {
return maybe(toPrimitive(x), y)
}
} else if (
typeof x === 'bigint' && typeof x === 'number' ||
typeof x === 'number' && typeof y === 'bigint'
) {
if (!isFinite(x) || !isFinite(y)) return false;
return Number(x) === Number(y)
}
return false;
}
void Object.assign(Object, { maybe: maybe.bind(Object.prototype) })
console.log(Object.maybe([], ''))
@indifferentghost
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment