Цель: Расширить определение типов данных / Сделать работу с типами более гибкими
Last active
December 1, 2023 07:44
-
-
Save dSalieri/25887b224dceffe2c97058705420ef77 to your computer and use it in GitHub Desktop.
Реализация специального объекта с определением типов данных
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Type = { | |
of(arg, type = "main") { | |
if (!["basic", "main", "complex"].some((v) => v === type)) { | |
throw Error("type argument has incorrect value"); | |
} | |
const basic = this.isObject(arg) ? "object" : "primitive"; | |
if (type === "basic") return basic; | |
const main = Object.prototype.toString.call(arg).slice(8, -1).toLowerCase(); | |
if (type === "main") return main; | |
/// Assert: type === "complex" | |
return `${basic} ${main}`; | |
}, | |
ofFunction(arg) { | |
if (typeof arg !== "function") return null; | |
const str = Function.prototype.toString.call(arg); | |
const parsed = str.slice(0, str.indexOf("(")); | |
if (parsed.includes("class")) return "class"; | |
if (parsed.includes("function")) return "function"; | |
if (arg.name.length > 0 && parsed.includes(arg.name)) return "method"; | |
return "arrow" | |
}, | |
ofPropertyDescriptor(obj, prop) { | |
const desc = Object.getOwnPropertyDescriptor(obj || {}, prop); | |
if (!desc) return; | |
const slots = { | |
value: Object.prototype.hasOwnProperty.call(desc, "value"), | |
writable: Object.prototype.hasOwnProperty.call(desc, "writable"), | |
get: typeof desc.get === "function", | |
set: typeof desc.set === "function", | |
}; | |
switch (true) { | |
case slots.value || slots.writable: { | |
return "data"; | |
} | |
case slots.get && slots.set: { | |
return "accessor"; | |
} | |
case slots.get: { | |
return "accessor:get"; | |
} | |
case slots.set: { | |
return "accessor:set"; | |
} | |
} | |
}, | |
isNumber(arg) { | |
return typeof arg === "number"; | |
}, | |
isFloat(arg) { | |
if (!this.isNumber(arg)) return false; | |
return String(arg).includes("."); | |
}, | |
isInteger(arg) { | |
return Number.isInteger(arg); | |
}, | |
isString(arg) { | |
return typeof arg === "string"; | |
}, | |
isSymbol(arg) { | |
return typeof arg === "symbol"; | |
}, | |
isBoolean(arg) { | |
return typeof arg === "boolean"; | |
}, | |
isNull(arg) { | |
return arg === null; | |
}, | |
isUndefined(arg) { | |
return arg === undefined; | |
}, | |
isBigInt(arg) { | |
return typeof arg === "bigint"; | |
}, | |
isNaN(arg) { | |
return arg !== arg; | |
}, | |
isInfinity(arg, sign) { | |
if (sign === "+") return arg === Infinity; | |
if (sign === "-") return arg === -Infinity; | |
return [Infinity, -Infinity].some((v) => arg === v); | |
}, | |
isFunction(arg) { | |
return typeof arg === "function"; | |
}, | |
isAsync(arg) { | |
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor; | |
return Object.getPrototypeOf(arg).constructor === AsyncFunction; | |
}, | |
isGenerator(arg) { | |
const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor; | |
return Object.getPrototypeOf(arg).constructor === GeneratorFunction; | |
}, | |
isAsyncGenerator(arg) { | |
const AsyncGeneratorFunction = Object.getPrototypeOf(async function*(){}).constructor; | |
return Object.getPrototypeOf(arg).constructor === AsyncGeneratorFunction; | |
}, | |
isConstructor(arg, secure = false) { | |
if (!this.isFunction(arg)) return false; | |
if (secure === false) { | |
/// fast, but unsecure | |
return this.isArrow(arg) || this.isMethod(arg) | |
? false | |
: arg.prototype.constructor === arg; | |
} else { | |
/// heavy, but secure | |
try { | |
new new Proxy(arg, {construct() { return {} }}); | |
} catch (e) { | |
return false; | |
} | |
return true; | |
} | |
}, | |
isArrow(arg) { | |
return this.ofFunction(arg) === "arrow"; | |
}, | |
isMethod(arg) { | |
return this.ofFunction(arg) === "method"; | |
}, | |
isOrdinary(arg) { | |
return this.ofFunction(arg) === "function"; | |
}, | |
isClass(arg) { | |
return this.ofFunction(arg) === "class"; | |
}, | |
isBuiltInFunction(arg) { | |
if (!this.isFunction(arg)) return false; | |
return Function.prototype.toString.call(arg).match(/{\s*\[native code\]\s*}$/) !== null; | |
}, | |
isDataProperty(obj, prop) { | |
return this.ofPropertyDescriptor(obj, prop) === "data"; | |
}, | |
isAccessorProperty(obj, prop) { | |
const type = this.ofPropertyDescriptor(obj, prop); | |
return type && type.includes("accessor"); | |
}, | |
isGetter(obj, prop) { | |
const type = this.ofPropertyDescriptor(obj, prop); | |
return ["accessor", "accessor:get"].some((value) => value === type); | |
}, | |
isSetter(obj, prop) { | |
const type = this.ofPropertyDescriptor(obj, prop); | |
return ["accessor", "accessor:set"].some((value) => value === type); | |
}, | |
isPrimitive(arg) { | |
return !this.isObject(arg); | |
}, | |
isObject(arg, isNotAFunction) { | |
const o = typeof arg === "object" && arg !== null; | |
if (isNotAFunction === true) return o; | |
return o || typeof arg === "function"; | |
}, | |
isPrimitiveInObject(arg) { | |
if (this.isPrimitive(arg)) return false; | |
switch (this.of(arg)) { | |
case "number": | |
case "string": | |
case "boolean": | |
case "bigint": | |
case "symbol": { | |
return true; | |
} | |
default: { | |
return false | |
} | |
} | |
}, | |
__proto__: { | |
[Symbol.toStringTag]: "Type", | |
}, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment