From c277c8bf8b747c830a7a594a85737225393422b9 Mon Sep 17 00:00:00 2001 From: pabadm Date: Fri, 22 Sep 2023 02:04:05 +0200 Subject: [PATCH 01/60] new implementation and advanced typescript --- index.d.ts | 8 -- index.js | 14 --- package.json | 11 ++- src/index.js | 23 +++++ src/test/esmodule.mjs | 216 +++++++++++++++++++++++++++++++++++++++++ src/test/require.cjs | 219 +++++++++++++++++++++++++++++++++++++++++ src/test/typeCheck.ts | 220 ++++++++++++++++++++++++++++++++++++++++++ src/types/index.d.ts | 35 +++++++ test/esmodule.mjs | 43 --------- test/require.js | 45 --------- test/types.ts | 37 ------- tsconfig.json | 128 +++++------------------- 12 files changed, 746 insertions(+), 253 deletions(-) delete mode 100644 index.d.ts delete mode 100644 index.js create mode 100644 src/index.js create mode 100644 src/test/esmodule.mjs create mode 100644 src/test/require.cjs create mode 100644 src/test/typeCheck.ts create mode 100644 src/types/index.d.ts delete mode 100644 test/esmodule.mjs delete mode 100644 test/require.js delete mode 100644 test/types.ts diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 9b89243..0000000 --- a/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -type Func = (...argv: Args) => Return; -interface ICallableInstance { - // prettier-ignore - new (property: string | symbol): - Func; -} -declare const CallableInstance: ICallableInstance; -export = CallableInstance; diff --git a/index.js b/index.js deleted file mode 100644 index 07490d3..0000000 --- a/index.js +++ /dev/null @@ -1,14 +0,0 @@ -function CallableInstance(property) { - var func = this.constructor.prototype[property]; - var apply = function () { - return func.apply(apply, arguments); - }; - Object.setPrototypeOf(apply, this.constructor.prototype); - Object.getOwnPropertyNames(func).forEach(function (p) { - Object.defineProperty(apply, p, Object.getOwnPropertyDescriptor(func, p)); - }); - return apply; -} -CallableInstance.prototype = Object.create(Function.prototype); - -module.exports = CallableInstance; diff --git a/package.json b/package.json index 4c1a9cb..b26f9f1 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,22 @@ { "name": "callable-instance", - "version": "2.0.0", + "version": "3.0.0", "description": "Instances of classes which are directly callable as functions.", "repository": "CGamesPlay/node-callable-instance", - "main": "index.js", + "main": "dist/index.js", "types": "index.d.ts", "scripts": { - "test": "mocha && tsc" + "build": "rm -rf ./dist && tsc", + "test": "mocha src/test && tsc" }, "keywords": [ "instance", "function", "object", "class", - "callable" + "callable", + "callable-instance", + "JS Callable Object" ], "bugs": { "url": "https://github.com/CGamesPlay/node-callable-instance/issues" diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..d07b346 --- /dev/null +++ b/src/index.js @@ -0,0 +1,23 @@ +export const CALL = Symbol("Callable.CALL") +const BOUND = Symbol("Callable.BOUND") + +class Callable extends Function { + static get CALL() { + return CALL + } + get [Symbol.toStringTag]() { + return "Callable" + } + constructor(property = CALL) { + super("...a", "return this.a[this.b][this.c](...a)") + this[BOUND] = this.bind({ + a: this, + b: BOUND, + c: property + }) + Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); + return this[BOUND] + } +} + +export default Callable \ No newline at end of file diff --git a/src/test/esmodule.mjs b/src/test/esmodule.mjs new file mode 100644 index 0000000..7bbe793 --- /dev/null +++ b/src/test/esmodule.mjs @@ -0,0 +1,216 @@ +import assert from "assert"; +import C, { CALL } from '../../dist/index.js' + +const Callable = C.default + +function getTitle(Class, isDefault) { + return `${Class.name}${isDefault ? ' default' : ''} (mjs)` +} + +describe(getTitle(Callable) + ' Callable Class Test', function () { + it('correctly inherits prototypes', function () { + assert(new Callable() instanceof Object) + assert(new Callable() instanceof Function) + assert(new Callable() instanceof Callable) + }) + it("Callable.CALL symbol is equal to CALL symbol", function () { + assert(Callable.CALL === CALL) + }) +}) + +function defaultTest(Class, instances = []) { + return describe(getTitle(Class, true), function () { + it('is callable', function () { + assert(typeof new Class('msg') === 'function') + assert((function () { + let test + try { + test = new Class('msg')() + } catch (e) { + return false + } + return true + })()) + }) + it('correctly inherits prototypes', function () { + + assert(typeof new Class('msg') === 'function') + const InstancesDefault = [Object, Function, Callable, Class] + for (let i = 0; i !== InstancesDefault.length; i++) { + assert(new Class('msg') instanceof InstancesDefault[i]) + } + for (let i = 0; i !== instances.length; i++) { + assert(new Class('msg') instanceof InstancesDefault[i]) + } + + }) + it('copies name property from constructor', function () { + assert(new Class('test').name === Class.name) + }) + it("has length property set to 0 due to ...args", function () { + assert(new Class("testing").length === 0); + }); + }) +} + + + +class MyTest extends Callable { + constructor(message) { + super("go"); + this.message = message; + } + + go(arg) { + return arg || this.message; + } +} + +defaultTest(MyTest) + +describe(getTitle(MyTest), function () { + it('has same return as go method', function () { + assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) + }); + it("correctly bounds this", function () { + const test = new MyTest('testing') + assert(test() === 'testing') + assert(test.go() === 'testing') + test.message = 'new message' + assert(test() === 'new message') + assert(test.go() === 'new message') + }); + it('has own string tag Callable', function () { + assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') + }) + it("supports property redefine", function () { + const obj = new MyTest('testing') + assert(obj() === obj.go()); + obj.go = () => { + return null + } + assert(obj() === null) + assert(obj.go() === null) + obj.go = function () { + return MyTest + } + assert(obj() === MyTest) + assert(obj.go() === MyTest) + }); +}); + +class MyTestExtended extends MyTest { + constructor(msg) { + super() + this.message = msg + } + go() { + return this.message + } +} + +defaultTest(MyTestExtended, [MyTest]) + +describe(getTitle(MyTestExtended), function () { + it('has same return as go method', function () { + assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) + }); + it("correctly bounds this", function () { + const test = new MyTest('testing') + assert(test() === 'testing') + assert(test.go() === 'testing') + test.message = 'new message' + assert(test() === 'new message') + assert(test.go() === 'new message') + }); + it('has own string tag Callable', function () { + assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') + }) + it("supports property redefine", function () { + const obj = new MyTestExtended('testing') + assert(obj() === obj.go()); + obj.go = () => { + return null + } + assert(obj() === null) + assert(obj.go() === null) + obj.go = function () { + return MyTestExtended + } + assert(obj() === MyTestExtended) + assert(obj.go() === MyTestExtended) + }); +}) + +class MyTestWithCall extends Callable { + constructor(message) { + super(); + this.message = message; + } + get [Symbol.toStringTag]() { + return 'Redefined' + } + go(arg) { + return arg || this.message; + } + [Callable.CALL](arg) { + return this.go(arg) + } + [CALL](arg) { + return this.go(arg) + } +} + +defaultTest(MyTestWithCall) + +describe(getTitle(MyTestWithCall), function () { + it('has same return as go method', function () { + assert(new MyTestWithCall('test').go('arg') === new MyTest('test')('arg')) + }); + it("correctly bounds this", function () { + const test = new MyTestWithCall('testing') + assert(test() === 'testing') + assert(test.go() === 'testing') + assert(test[CALL]() === 'testing') + assert(test[Callable.CALL]() === 'testing') + test.message = 'new message' + assert(test() === 'new message') + assert(test.go() === 'new message') + assert(test[CALL]() === 'new message') + assert(test[Callable.CALL]() === 'new message') + test.go = () => 'different' + assert(test.go() === 'different') + assert(test[CALL]() === 'different') + assert(test[Callable.CALL]() === 'different') + test[CALL] = () => 'called' + assert(test.go() === 'different') + assert(test[CALL]() === 'called') + assert(test[Callable.CALL]() === 'called') + assert(test.message === 'new message') + }); + it('has own string tag Redefined', function () { + assert(Object.prototype.toString.call(new MyTestWithCall('test')) === '[object Redefined]') + }) + it("supports property redefine", function () { + const obj = new MyTestWithCall('testing') + assert(obj() === obj.go()); + obj.go = () => { + return null + } + assert(obj() === null) + assert(obj.go() === null) + obj.go = function () { + return MyTestWithCall + } + assert(obj() === MyTestWithCall) + assert(obj.go() === MyTestWithCall) + obj[CALL] = function () { + return 'check' + } + assert(obj() === 'check') + assert(obj.go() === MyTestWithCall) + }); + it('has only one [Callable.CALL] method', function () { + assert(MyTestWithCall[CALL] === MyTestWithCall[Callable.CALL] && Callable.CALL === CALL) + }) +}); diff --git a/src/test/require.cjs b/src/test/require.cjs new file mode 100644 index 0000000..8699082 --- /dev/null +++ b/src/test/require.cjs @@ -0,0 +1,219 @@ +"use strict"; + +var assert = require("assert"); +var C = require("../../dist/index.js"); + +var CALL = C.CALL +var Callable = C.default + +function getTitle(Class, isDefault) { + return `${Class.name}${isDefault ? ' default' : ''} (cjs)` +} + +describe(getTitle(Callable) + ' Callable Class Test', function () { + it('correctly inherits prototypes', function () { + assert(new Callable() instanceof Object) + assert(new Callable() instanceof Function) + assert(new Callable() instanceof Callable) + }) + it("Callable.CALL symbol is equal to CALL symbol", function () { + assert(Callable.CALL === CALL) + }) +}) + +function defaultTest(Class, instances = []) { + return describe(getTitle(Class, true), function () { + it('is callable', function () { + assert(typeof new Class('msg') === 'function') + assert((function () { + let test + try { + test = new Class('msg')() + } catch (e) { + return false + } + return true + })()) + }) + it('correctly inherits prototypes', function () { + + assert(typeof new Class('msg') === 'function') + const InstancesDefault = [Object, Function, Callable, Class] + for (let i = 0; i !== InstancesDefault.length; i++) { + assert(new Class('msg') instanceof InstancesDefault[i]) + } + for (let i = 0; i !== instances.length; i++) { + assert(new Class('msg') instanceof InstancesDefault[i]) + } + + }) + it('copies name property from constructor', function () { + assert(new Class('test').name === Class.name) + }) + it("has length property set to 0 due to ...args", function () { + assert(new Class("testing").length === 0); + }); + }) +} + + + +class MyTest extends Callable { + constructor(message) { + super("go"); + this.message = message; + } + + go(arg) { + return arg || this.message; + } +} + +defaultTest(MyTest) + +describe(getTitle(MyTest), function () { + it('has same return as go method', function () { + assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) + }); + it("correctly bounds this", function () { + const test = new MyTest('testing') + assert(test() === 'testing') + assert(test.go() === 'testing') + test.message = 'new message' + assert(test() === 'new message') + assert(test.go() === 'new message') + }); + it('has own string tag Callable', function () { + assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') + }) + it("supports property redefine", function () { + const obj = new MyTest('testing') + assert(obj() === obj.go()); + obj.go = () => { + return null + } + assert(obj() === null) + assert(obj.go() === null) + obj.go = function () { + return MyTest + } + assert(obj() === MyTest) + assert(obj.go() === MyTest) + }); +}); + +class MyTestExtended extends MyTest { + constructor(msg) { + super() + this.message = msg + } + go() { + return this.message + } +} + +defaultTest(MyTestExtended, [MyTest]) + +describe(getTitle(MyTestExtended), function () { + it('has same return as go method', function () { + assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) + }); + it("correctly bounds this", function () { + const test = new MyTest('testing') + assert(test() === 'testing') + assert(test.go() === 'testing') + test.message = 'new message' + assert(test() === 'new message') + assert(test.go() === 'new message') + }); + it('has own string tag Callable', function () { + assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') + }) + it("supports property redefine", function () { + const obj = new MyTestExtended('testing') + assert(obj() === obj.go()); + obj.go = () => { + return null + } + assert(obj() === null) + assert(obj.go() === null) + obj.go = function () { + return MyTestExtended + } + assert(obj() === MyTestExtended) + assert(obj.go() === MyTestExtended) + }); +}) + +class MyTestWithCall extends Callable { + constructor(message) { + super(); + this.message = message; + } + get [Symbol.toStringTag]() { + return 'Redefined' + } + go(arg) { + return arg || this.message; + } + [Callable.CALL](arg) { + return this.go(arg) + } + [CALL](arg) { + return this.go(arg) + } +} + +defaultTest(MyTestWithCall) + +describe(getTitle(MyTestWithCall), function () { + it('has same return as go method', function () { + assert(new MyTestWithCall('test').go('arg') === new MyTest('test')('arg')) + }); + it("correctly bounds this", function () { + const test = new MyTestWithCall('testing') + assert(test() === 'testing') + assert(test.go() === 'testing') + assert(test[CALL]() === 'testing') + assert(test[Callable.CALL]() === 'testing') + test.message = 'new message' + assert(test() === 'new message') + assert(test.go() === 'new message') + assert(test[CALL]() === 'new message') + assert(test[Callable.CALL]() === 'new message') + test.go = () => 'different' + assert(test.go() === 'different') + assert(test[CALL]() === 'different') + assert(test[Callable.CALL]() === 'different') + test[CALL] = () => 'called' + assert(test.go() === 'different') + assert(test[CALL]() === 'called') + assert(test[Callable.CALL]() === 'called') + assert(test.message === 'new message') + }); + it('has own string tag Redefined', function () { + assert(Object.prototype.toString.call(new MyTestWithCall('test')) === '[object Redefined]') + }) + it("supports property redefine", function () { + const obj = new MyTestWithCall('testing') + assert(obj() === obj.go()); + obj.go = () => { + return null + } + assert(obj() === null) + assert(obj.go() === null) + obj.go = function () { + return MyTestWithCall + } + assert(obj() === MyTestWithCall) + assert(obj.go() === MyTestWithCall) + obj[CALL] = function () { + return 'check' + } + assert(obj() === 'check') + assert(obj.go() === MyTestWithCall) + }); + it('has only one [Callable.CALL] method', function () { + assert(MyTestWithCall[CALL] === MyTestWithCall[Callable.CALL] && Callable.CALL === CALL) + }) +}); diff --git a/src/test/typeCheck.ts b/src/test/typeCheck.ts new file mode 100644 index 0000000..f5ef486 --- /dev/null +++ b/src/test/typeCheck.ts @@ -0,0 +1,220 @@ +import { expectType } from "ts-expect"; + +import Callable, { CALL, RedefineCall } from "callable-instance"; + +class RepeaterWithFuncGeneric extends Callable<(x: string) => string, 'go'> { + constructor(public count: number) { + super('go'); + } + + go(arg: string): string { + return arg.repeat(this.count); + } +} + +describe("Callable With Func Generic and custom property (TypeScript)", function () { + + it("is callable", function () { + expectType<(x: string) => string>(new RepeaterWithFuncGeneric(1)); + // @ts-expect-error wrong type for constructor + new RepeaterWithFuncGeneric("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithFuncGeneric(5).go(5); + // Valid propert access. + new RepeaterWithFuncGeneric(5).count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithFuncGeneric(5)); + expectType<(x: string) => string>(new RepeaterWithFuncGeneric(5).go); + }); + + it("is an instance of Repeater", function () { + expectType(new RepeaterWithFuncGeneric(5)); + //expectType(new Repeater(5)); + expectType(new RepeaterWithFuncGeneric(5)); + expectType(new RepeaterWithFuncGeneric(5)); + }); +}); + +interface IRepeaterWithCall { + [CALL](arg: string): string +} + +class RepeaterWithCALL extends Callable { + constructor(public count: number) { + super(); + } + + go(arg: string): string { + return arg.repeat(this.count); + } + + // [CALL] = '234' + [CALL](arg: string): string { + return this.go(arg) + } +} + +class RepeaterWithTest extends Callable<(arg: 'test') => string> { + constructor(public count: number) { + super(); + } + + go(arg: string): string { + return arg.repeat(this.count); + } + + // [CALL] = '234' + [CALL](arg: string): string { + return this.go(arg) + } +} + +const test23 = new RepeaterWithTest(23) +const ggg = test23('test') + +const test = new RepeaterWithCALL(4) +test.go('23') +// const TTTTTT = test.CALL +const test2 = test('23') + +describe("Callable With CALL Generic (TypeScript)", function () { + it("is callable", function () { + expectType<(x: string) => string>(new RepeaterWithCALL(1)); + // @ts-expect-error wrong type for constructor + new RepeaterWithCALL("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithCALL(5).go(5); + // Valid propert access. + new RepeaterWithCALL(5).count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithCALL(5)); + expectType<(x: string) => string>(new RepeaterWithCALL(5).go); + }); + + it("is an instance of Repeater", function () { + expectType(new RepeaterWithCALL(5)); + //expectType(new Repeater(5)); + expectType(new RepeaterWithCALL(5)); + expectType(new RepeaterWithCALL(5)); + }); +}); + +interface IRepeaterWithCallableCall { + // go: (arg: string): string + [Callable.CALL](arg: string): string + // [Callable.CALL]: string + +} + +class RepeaterWithCallableCALL extends Callable { + constructor(public count: number) { + super(); + } + + go(arg: string): string { + return arg.repeat(this.count); + } + + [Callable.CALL](arg: string): string { + return this.go(arg) + } +} + +const check = new RepeaterWithCallableCALL(23) +check('23') + +describe("Callable With Callable.CALL Generic (TypeScript)", function () { + it("is callable", function () { + expectType<(x: string) => string>(new RepeaterWithCallableCALL(1)); + // @ts-expect-error wrong type for constructor + new RepeaterWithCallableCALL("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithCallableCALL(5).go(5); + // Valid propert access. + new RepeaterWithCallableCALL(5).count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithCallableCALL(5)); + expectType<(x: string) => string>(new RepeaterWithCallableCALL(5).go); + }); + + it("is an instance of Repeater", function () { + expectType(new RepeaterWithCallableCALL(5)); + //expectType(new Repeater(5)); + expectType(new RepeaterWithCallableCALL(5)); + expectType(new RepeaterWithCallableCALL(5)); + }); +}); + +interface IRepeaterWithCustomProperty { + $call(arg: string): string +} + +class RepeaterWithCustomProperty extends Callable { + constructor(public count: number) { + super("$call"); + } + + go(arg: string): string { + return arg.repeat(this.count); + } + + $call(arg: string): string { + return this.go(arg) + } +} + +describe("Callable With Custom Property Generic (TypeScript)", function () { + it("is callable", function () { + expectType<(x: string) => string>(new RepeaterWithCustomProperty(1)); + // @ts-expect-error wrong type for constructor + new RepeaterWithCustomProperty("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithCustomProperty(5).go(5); + // Valid propert access. + new RepeaterWithCustomProperty(5).count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithCustomProperty(5)); + expectType<(x: string) => string>(new RepeaterWithCustomProperty(5).go); + }); + + it("is an instance of Repeater", function () { + expectType(new RepeaterWithCustomProperty(5)); + //expectType(new Repeater(5)); + expectType(new RepeaterWithCustomProperty(5)); + expectType(new RepeaterWithCustomProperty(5)); + }); +}); + + +class TestCallable extends Callable{ + constructor() { + super('test') + } + + test() { + return 'test' + } +} + +const testst = new TestCallable() + +class TestCallableExtends extends (TestCallable as RedefineCall){ + constructor() { + super() + } + test() { + return 23 + } +} + +const newewe = new TestCallableExtends() +newewe.test() +const ttt = newewe() \ No newline at end of file diff --git a/src/types/index.d.ts b/src/types/index.d.ts new file mode 100644 index 0000000..f42eac7 --- /dev/null +++ b/src/types/index.d.ts @@ -0,0 +1,35 @@ +declare module 'callable-instance' { + export const CALL: unique symbol; + + type BaseProperty = symbol | string; + type CustomProperty = Exclude; + + type BaseFunc = (...args: any) => any; + type BaseClass = abstract new (...args: any) => any; + type BaseInterface = Record; + + type ExtractFuncFromInterface = I[P] extends BaseFunc ? I[P] : never; + + interface CloneFuncFromClass { + (...args: Parameters[P]>): ReturnType[P]>; + } + + type ExtractFunc = C extends BaseFunc + ? C + : C extends BaseClass + ? CloneFuncFromClass + : ExtractFuncFromInterface; + + interface CallableConstructor { + readonly CALL: typeof CALL; + new (property: P): ExtractFunc; + new (): ExtractFunc; + } + + export interface RedefineCall { + new (...args: ConstructorParameters): Omit, P> & ExtractFunc; + } + + const Callable: CallableConstructor; + export default Callable; +} \ No newline at end of file diff --git a/test/esmodule.mjs b/test/esmodule.mjs deleted file mode 100644 index dea4397..0000000 --- a/test/esmodule.mjs +++ /dev/null @@ -1,43 +0,0 @@ -import assert from "assert"; -import CallableInstance from "../index.js"; - -class MyTest extends CallableInstance { - constructor(message) { - super("go"); - this.message = message; - } - - go(arg) { - return arg || this.message; - } -} - -describe("CallableInstance (mjs)", function () { - it("is callable", function () { - assert(new MyTest("testing")() === "testing"); - assert(new MyTest()("arg") === "arg"); - }); - - it("is an object", function () { - assert(new MyTest("testing").go() === "testing"); - }); - - it("is an instance of MyTest", function () { - assert(new MyTest("testing") instanceof MyTest); - assert(new MyTest("testing") instanceof CallableInstance); - assert(new MyTest("testing") instanceof Function); - assert(new MyTest("testing") instanceof Object); - }); - - it("is a function", function () { - assert(typeof new MyTest("testing") === "function"); - }); - - it("copies the name property", function () { - assert(new MyTest("testing").name === "go"); - }); - - it("copies the length property", function () { - assert(new MyTest("testing").length === 1); - }); -}); diff --git a/test/require.js b/test/require.js deleted file mode 100644 index 3c1b879..0000000 --- a/test/require.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; - -var assert = require("assert"); -var CallableInstance = require("../index"); - -class MyTest extends CallableInstance { - constructor(message) { - super("go"); - this.message = message; - } - - go(arg) { - return arg || this.message; - } -} - -describe("CallableInstance (require)", function () { - it("is callable", function () { - assert(new MyTest("testing")() === "testing"); - assert(new MyTest()("arg") === "arg"); - }); - - it("is an object", function () { - assert(new MyTest("testing").go() === "testing"); - }); - - it("is an instance of MyTest", function () { - assert(new MyTest("testing") instanceof MyTest); - assert(new MyTest("testing") instanceof CallableInstance); - assert(new MyTest("testing") instanceof Function); - assert(new MyTest("testing") instanceof Object); - }); - - it("is a function", function () { - assert(typeof new MyTest("testing") === "function"); - }); - - it("copies the name property", function () { - assert(new MyTest("testing").name === "go"); - }); - - it("copies the length property", function () { - assert(new MyTest("testing").length === 1); - }); -}); diff --git a/test/types.ts b/test/types.ts deleted file mode 100644 index 721b08c..0000000 --- a/test/types.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { expectType } from "ts-expect"; - -import CallableInstance from "../index.js"; - -class Repeater extends CallableInstance<[string], string> { - constructor(public count: number) { - super("go"); - } - - go(arg: string): string { - return arg.repeat(this.count); - } -} - -describe("CallableInstance (TypeScript)", function () { - it("is callable", function () { - expectType<(x: string) => string>(new Repeater(1)); - // @ts-expect-error wrong type for constructor - new Repeater("testing"); - // @ts-expect-error wrong type for method - new Repeater(5).go(5); - // Valid propert access. - new Repeater(5).count = 4; - }); - - it("is an object", function () { - expectType(new Repeater(5)); - expectType<(x: string) => string>(new Repeater(5).go); - }); - - it("is an instance of Repeater", function () { - expectType(new Repeater(5)); - //expectType(new Repeater(5)); - expectType(new Repeater(5)); - expectType(new Repeater(5)); - }); -}); diff --git a/tsconfig.json b/tsconfig.json index f487273..c592425 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,103 +1,27 @@ { - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs" /* Specify what module code is generated. */, - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - "noEmit": true /* Disable emitting files from a compilation. */, - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} + "compilerOptions": { + "outDir": "./dist", + "target": "es6", + "allowJs": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "noImplicitAny": false, + "removeComments": true, + "declaration": false, + "sourceMap": true, + "typeRoots": [ + "node_modules/@types", "src/types" + ] + }, + "include": [ + "src" + ], + "exclude": [ + "test", + "src/types", + "src/test", + "lib", + "node_modules" + ] +} \ No newline at end of file From e31439c8df6172288ff1f9744929853d2e1783c8 Mon Sep 17 00:00:00 2001 From: pabadm Date: Fri, 22 Sep 2023 02:05:10 +0200 Subject: [PATCH 02/60] new implementation and advanced typescript --- .gitignore | 4 +--- dist/index.js | 25 +++++++++++++++++++++++++ dist/index.js.map | 1 + 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 dist/index.js create mode 100644 dist/index.js.map diff --git a/.gitignore b/.gitignore index 56f6477..e9a3d6e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,4 @@ jspm_packages .npm # Optional REPL history -.node_repl_history - -/dist +.node_repl_history \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..ed22814 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CALL = void 0; +exports.CALL = Symbol("Callable.CALL"); +const BOUND = Symbol("Callable.BOUND"); +class Callable extends Function { + static get CALL() { + return exports.CALL; + } + get [Symbol.toStringTag]() { + return "Callable"; + } + constructor(property = exports.CALL) { + super("...a", "return this.a[this.b][this.c](...a)"); + this[BOUND] = this.bind({ + a: this, + b: BOUND, + c: property + }); + Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); + return this[BOUND]; + } +} +exports.default = Callable; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map new file mode 100644 index 0000000..be0cfc0 --- /dev/null +++ b/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":";;;AAAa,QAAA,IAAI,GAAG,MAAM,CAAC,eAAe,CAAC,CAAA;AAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAA;AAEtC,MAAM,QAAS,SAAQ,QAAQ;IAC7B,MAAM,KAAK,IAAI;QACb,OAAO,YAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACtB,OAAO,UAAU,CAAA;IACnB,CAAC;IACD,YAAY,QAAQ,GAAG,YAAI;QACzB,KAAK,CAAC,MAAM,EAAE,qCAAqC,CAAC,CAAA;QACpD,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;YACtB,CAAC,EAAE,IAAI;YACP,CAAC,EAAE,KAAK;YACR,CAAC,EAAE,QAAQ;SACZ,CAAC,CAAA;QACF,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,wBAAwB,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QACtG,OAAO,IAAI,CAAC,KAAK,CAAC,CAAA;IACpB,CAAC;CACF;AAED,kBAAe,QAAQ,CAAA"} \ No newline at end of file From 45e290077883e8c87920d6068a62395f9edaa17a Mon Sep 17 00:00:00 2001 From: pabadm Date: Fri, 22 Sep 2023 02:49:26 +0200 Subject: [PATCH 03/60] exported types --- package-lock.json | 4 ++-- package.json | 2 +- src/types/index.d.ts | 13 ++++++++----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index cf71b44..0f4292f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "callable-instance", - "version": "2.0.0", + "version": "3.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "callable-instance", - "version": "2.0.0", + "version": "3.0.0", "license": "MIT", "devDependencies": { "@types/mocha": "^10.0.1", diff --git a/package.json b/package.json index b26f9f1..703293d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Instances of classes which are directly callable as functions.", "repository": "CGamesPlay/node-callable-instance", "main": "dist/index.js", - "types": "index.d.ts", + "types": "src/types/index.d.ts", "scripts": { "build": "rm -rf ./dist && tsc", "test": "mocha src/test && tsc" diff --git a/src/types/index.d.ts b/src/types/index.d.ts index f42eac7..e7cbbe0 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -1,8 +1,9 @@ declare module 'callable-instance' { export const CALL: unique symbol; + export type SCALL = typeof CALL type BaseProperty = symbol | string; - type CustomProperty = Exclude; + type CustomProperty = Exclude; type BaseFunc = (...args: any) => any; type BaseClass = abstract new (...args: any) => any; @@ -10,6 +11,7 @@ declare module 'callable-instance' { type ExtractFuncFromInterface = I[P] extends BaseFunc ? I[P] : never; + // to avoid error (recursively references itself as a base type) interface CloneFuncFromClass { (...args: Parameters[P]>): ReturnType[P]>; } @@ -20,14 +22,15 @@ declare module 'callable-instance' { ? CloneFuncFromClass : ExtractFuncFromInterface; - interface CallableConstructor { - readonly CALL: typeof CALL; + export interface CallableConstructor { + get CALL(): SCALL; new (property: P): ExtractFunc; - new (): ExtractFunc; + new (): ExtractFunc; } export interface RedefineCall { - new (...args: ConstructorParameters): Omit, P> & ExtractFunc; + new (...args: ConstructorParameters): + Omit, P> & ExtractFunc; } const Callable: CallableConstructor; From c7e3ab853afe7e58e9bd20ecfb9d16954b9c211f Mon Sep 17 00:00:00 2001 From: pabadm Date: Tue, 26 Sep 2023 00:55:34 +0200 Subject: [PATCH 04/60] added cjs and mjs support --- dist/{ => cjs}/index.js | 11 +- dist/cjs/package.json | 3 + dist/index.js.map | 1 - dist/mjs/index.js | 21 + dist/mjs/package.json | 3 + fixup.sh | 10 + {src/types => lib}/index.d.ts | 26 +- package-lock.json | 1171 +++++++++++---------------- package.json | 21 +- src/index.js | 17 +- src/test/require.cjs | 219 ----- src/test/test.cjs | 216 +++++ src/test/{esmodule.mjs => test.mjs} | 4 +- src/test/typeCheck.ts | 31 +- tsconfig-cjs.json | 8 + tsconfig.json | 31 +- 16 files changed, 840 insertions(+), 953 deletions(-) rename dist/{ => cjs}/index.js (55%) create mode 100644 dist/cjs/package.json delete mode 100644 dist/index.js.map create mode 100644 dist/mjs/index.js create mode 100644 dist/mjs/package.json create mode 100755 fixup.sh rename {src/types => lib}/index.d.ts (64%) delete mode 100644 src/test/require.cjs create mode 100644 src/test/test.cjs rename src/test/{esmodule.mjs => test.mjs} (98%) create mode 100644 tsconfig-cjs.json diff --git a/dist/index.js b/dist/cjs/index.js similarity index 55% rename from dist/index.js rename to dist/cjs/index.js index ed22814..3f64f5e 100644 --- a/dist/index.js +++ b/dist/cjs/index.js @@ -1,8 +1,8 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CALL = void 0; -exports.CALL = Symbol("Callable.CALL"); -const BOUND = Symbol("Callable.BOUND"); +exports.CALL = Symbol("Callable.CALL"); // CALL symbol +const BOUND = Symbol("Callable.BOUND"); // unique symbol for binding function class Callable extends Function { static get CALL() { return exports.CALL; @@ -11,15 +11,14 @@ class Callable extends Function { return "Callable"; } constructor(property = exports.CALL) { - super("...a", "return this.a[this.b][this.c](...a)"); + super("...a", "return this.a[this.b][this.c](...a)"); // calls property from child class ( function (...args)){ return this[BOUND][property](...args)}) this[BOUND] = this.bind({ a: this, b: BOUND, c: property }); - Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); - return this[BOUND]; + Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); // copies constructor name + return this[BOUND]; // returns Bound function } } exports.default = Callable; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/cjs/package.json b/dist/cjs/package.json new file mode 100644 index 0000000..1cd945a --- /dev/null +++ b/dist/cjs/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/dist/index.js.map b/dist/index.js.map deleted file mode 100644 index be0cfc0..0000000 --- a/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":";;;AAAa,QAAA,IAAI,GAAG,MAAM,CAAC,eAAe,CAAC,CAAA;AAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAA;AAEtC,MAAM,QAAS,SAAQ,QAAQ;IAC7B,MAAM,KAAK,IAAI;QACb,OAAO,YAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACtB,OAAO,UAAU,CAAA;IACnB,CAAC;IACD,YAAY,QAAQ,GAAG,YAAI;QACzB,KAAK,CAAC,MAAM,EAAE,qCAAqC,CAAC,CAAA;QACpD,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;YACtB,CAAC,EAAE,IAAI;YACP,CAAC,EAAE,KAAK;YACR,CAAC,EAAE,QAAQ;SACZ,CAAC,CAAA;QACF,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,wBAAwB,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QACtG,OAAO,IAAI,CAAC,KAAK,CAAC,CAAA;IACpB,CAAC;CACF;AAED,kBAAe,QAAQ,CAAA"} \ No newline at end of file diff --git a/dist/mjs/index.js b/dist/mjs/index.js new file mode 100644 index 0000000..10d9592 --- /dev/null +++ b/dist/mjs/index.js @@ -0,0 +1,21 @@ +export const CALL = Symbol("Callable.CALL"); // CALL symbol +const BOUND = Symbol("Callable.BOUND"); // unique symbol for binding function +class Callable extends Function { + static get CALL() { + return CALL; + } + get [Symbol.toStringTag]() { + return "Callable"; + } + constructor(property = CALL) { + super("...a", "return this.a[this.b][this.c](...a)"); // calls property from child class ( function (...args)){ return this[BOUND][property](...args)}) + this[BOUND] = this.bind({ + a: this, + b: BOUND, + c: property + }); + Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); // copies constructor name + return this[BOUND]; // returns Bound function + } +} +export default Callable; diff --git a/dist/mjs/package.json b/dist/mjs/package.json new file mode 100644 index 0000000..4720025 --- /dev/null +++ b/dist/mjs/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/fixup.sh b/fixup.sh new file mode 100755 index 0000000..c0556e6 --- /dev/null +++ b/fixup.sh @@ -0,0 +1,10 @@ +cat >dist/cjs/package.json <dist/mjs/package.json <; type BaseFunc = (...args: any) => any; type BaseClass = abstract new (...args: any) => any; - type BaseInterface = Record; + type BaseInterface = { + [k: BaseProperty]: any + }; type ExtractFuncFromInterface = I[P] extends BaseFunc ? I[P] : never; @@ -16,22 +18,24 @@ declare module 'callable-instance' { (...args: Parameters[P]>): ReturnType[P]>; } - type ExtractFunc = C extends BaseFunc - ? C - : C extends BaseClass + type ExtractFunc = C extends BaseClass ? CloneFuncFromClass - : ExtractFuncFromInterface; + : C extends BaseFunc + ? C + : C extends BaseInterface + ? ExtractFuncFromInterface + : never export interface CallableConstructor { - get CALL(): SCALL; + readonly CALL: SCALL; new (property: P): ExtractFunc; new (): ExtractFunc; } - export interface RedefineCall { - new (...args: ConstructorParameters): - Omit, P> & ExtractFunc; - } + export type OverrideCall = { + new (...args: ConstructorParameters): + Pick, BaseProperty> & ExtractFunc + } & Omit const Callable: CallableConstructor; export default Callable; diff --git a/package-lock.json b/package-lock.json index 0f4292f..18a4793 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "callable-instance", "version": "3.0.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -9,6 +9,7 @@ "version": "3.0.0", "license": "MIT", "devDependencies": { + "@types/jest": "^29.5.5", "@types/mocha": "^10.0.1", "mocha": "^10.2.0", "prettier": "^2.8.1", @@ -16,12 +17,298 @@ "typescript": "^4.9.4" } }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.5", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz", + "integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "node_modules/@types/mocha": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, + "node_modules/@types/node": { + "version": "20.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.5.tgz", + "integrity": "sha512-2qGq5LAOTh9izcc0+F+dToFigBWiK1phKPt7rNhOqJSr35y8rlIBjDwGtFSgAI6MGIhjwOVNSQZVdJsZJ2uR1w==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.25", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.25.tgz", + "integrity": "sha512-gy7iPgwnzNvxgAEi2bXOHWCVOG6f7xsprVJH4MjlAWeBmJ7vh/Y1kwMtUrs64ztf24zVIRCpr3n/z6gm9QIkgg==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==", + "dev": true + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -183,6 +470,21 @@ "fsevents": "~2.3.2" } }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -262,6 +564,15 @@ "node": ">=0.3.1" } }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -289,6 +600,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -332,20 +659,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -409,6 +722,12 @@ "node": "*" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -515,6 +834,88 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -558,6 +959,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", @@ -721,6 +1135,32 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -730,6 +1170,12 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -780,6 +1226,36 @@ "randombytes": "^2.1.0" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -956,670 +1432,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", - "dev": true - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "prettier": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", - "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "ts-expect": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-expect/-/ts-expect-1.3.0.tgz", - "integrity": "sha512-e4g0EJtAjk64xgnFPD6kTBUtpnMVzDrMb12N1YZV0VvSlhnVT3SGxiYTLdGy8Q5cYHOIC/FAHmZ10eGrAguicQ==", - "dev": true - }, - "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", - "dev": true - }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/package.json b/package.json index 703293d..8d9f205 100644 --- a/package.json +++ b/package.json @@ -3,20 +3,30 @@ "version": "3.0.0", "description": "Instances of classes which are directly callable as functions.", "repository": "CGamesPlay/node-callable-instance", - "main": "dist/index.js", - "types": "src/types/index.d.ts", + "main": "./dist/cjs/index.js", + "module": "./dist/mjs/index.js", + "types": "lib/index.d.ts", + "exports": { + "import": "./dist/mjs/index.js", + "require": "./dist/cjs/index.js" + }, "scripts": { - "build": "rm -rf ./dist && tsc", - "test": "mocha src/test && tsc" + "build": "rm -rf dist && tsc && tsc -p tsconfig-cjs.json && ./fixup.sh && npm link && npm link callable-instance", + "test": "npm run build && mocha src/test" }, "keywords": [ "instance", "function", "object", "class", + "constructor", "callable", "callable-instance", - "JS Callable Object" + "callable-object", + "callable-class", + "node-callable-instance", + "es6-callable", + "callable-constructor" ], "bugs": { "url": "https://github.com/CGamesPlay/node-callable-instance/issues" @@ -24,6 +34,7 @@ "author": "Ryan Patterson", "license": "MIT", "devDependencies": { + "@types/jest": "^29.5.5", "@types/mocha": "^10.0.1", "mocha": "^10.2.0", "prettier": "^2.8.1", diff --git a/src/index.js b/src/index.js index d07b346..0adaf6b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,22 +1,21 @@ -export const CALL = Symbol("Callable.CALL") -const BOUND = Symbol("Callable.BOUND") - +export const CALL = Symbol("Callable.CALL") // CALL symbol +const BOUND = Symbol("Callable.BOUND") // unique symbol for binding function class Callable extends Function { - static get CALL() { + static get CALL() { // getter for CALL symbol return CALL } - get [Symbol.toStringTag]() { + get [Symbol.toStringTag]() { // creates stringTag (Object.prototype.toString.call(new Callable()) === [object Callable]) return "Callable" } constructor(property = CALL) { - super("...a", "return this.a[this.b][this.c](...a)") - this[BOUND] = this.bind({ + super("...a", "return this.a[this.b][this.c](...a)") // calls property from child class ( function (...args)){ return this[BOUND][property](...args)}) + this[BOUND] = this.bind({ // binds to property, bound symbol and this a: this, b: BOUND, c: property }) - Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); - return this[BOUND] + Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); // copies constructor name + return this[BOUND] // returns Bound function } } diff --git a/src/test/require.cjs b/src/test/require.cjs deleted file mode 100644 index 8699082..0000000 --- a/src/test/require.cjs +++ /dev/null @@ -1,219 +0,0 @@ -"use strict"; - -var assert = require("assert"); -var C = require("../../dist/index.js"); - -var CALL = C.CALL -var Callable = C.default - -function getTitle(Class, isDefault) { - return `${Class.name}${isDefault ? ' default' : ''} (cjs)` -} - -describe(getTitle(Callable) + ' Callable Class Test', function () { - it('correctly inherits prototypes', function () { - assert(new Callable() instanceof Object) - assert(new Callable() instanceof Function) - assert(new Callable() instanceof Callable) - }) - it("Callable.CALL symbol is equal to CALL symbol", function () { - assert(Callable.CALL === CALL) - }) -}) - -function defaultTest(Class, instances = []) { - return describe(getTitle(Class, true), function () { - it('is callable', function () { - assert(typeof new Class('msg') === 'function') - assert((function () { - let test - try { - test = new Class('msg')() - } catch (e) { - return false - } - return true - })()) - }) - it('correctly inherits prototypes', function () { - - assert(typeof new Class('msg') === 'function') - const InstancesDefault = [Object, Function, Callable, Class] - for (let i = 0; i !== InstancesDefault.length; i++) { - assert(new Class('msg') instanceof InstancesDefault[i]) - } - for (let i = 0; i !== instances.length; i++) { - assert(new Class('msg') instanceof InstancesDefault[i]) - } - - }) - it('copies name property from constructor', function () { - assert(new Class('test').name === Class.name) - }) - it("has length property set to 0 due to ...args", function () { - assert(new Class("testing").length === 0); - }); - }) -} - - - -class MyTest extends Callable { - constructor(message) { - super("go"); - this.message = message; - } - - go(arg) { - return arg || this.message; - } -} - -defaultTest(MyTest) - -describe(getTitle(MyTest), function () { - it('has same return as go method', function () { - assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) - }); - it("correctly bounds this", function () { - const test = new MyTest('testing') - assert(test() === 'testing') - assert(test.go() === 'testing') - test.message = 'new message' - assert(test() === 'new message') - assert(test.go() === 'new message') - }); - it('has own string tag Callable', function () { - assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') - }) - it("supports property redefine", function () { - const obj = new MyTest('testing') - assert(obj() === obj.go()); - obj.go = () => { - return null - } - assert(obj() === null) - assert(obj.go() === null) - obj.go = function () { - return MyTest - } - assert(obj() === MyTest) - assert(obj.go() === MyTest) - }); -}); - -class MyTestExtended extends MyTest { - constructor(msg) { - super() - this.message = msg - } - go() { - return this.message - } -} - -defaultTest(MyTestExtended, [MyTest]) - -describe(getTitle(MyTestExtended), function () { - it('has same return as go method', function () { - assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) - }); - it("correctly bounds this", function () { - const test = new MyTest('testing') - assert(test() === 'testing') - assert(test.go() === 'testing') - test.message = 'new message' - assert(test() === 'new message') - assert(test.go() === 'new message') - }); - it('has own string tag Callable', function () { - assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') - }) - it("supports property redefine", function () { - const obj = new MyTestExtended('testing') - assert(obj() === obj.go()); - obj.go = () => { - return null - } - assert(obj() === null) - assert(obj.go() === null) - obj.go = function () { - return MyTestExtended - } - assert(obj() === MyTestExtended) - assert(obj.go() === MyTestExtended) - }); -}) - -class MyTestWithCall extends Callable { - constructor(message) { - super(); - this.message = message; - } - get [Symbol.toStringTag]() { - return 'Redefined' - } - go(arg) { - return arg || this.message; - } - [Callable.CALL](arg) { - return this.go(arg) - } - [CALL](arg) { - return this.go(arg) - } -} - -defaultTest(MyTestWithCall) - -describe(getTitle(MyTestWithCall), function () { - it('has same return as go method', function () { - assert(new MyTestWithCall('test').go('arg') === new MyTest('test')('arg')) - }); - it("correctly bounds this", function () { - const test = new MyTestWithCall('testing') - assert(test() === 'testing') - assert(test.go() === 'testing') - assert(test[CALL]() === 'testing') - assert(test[Callable.CALL]() === 'testing') - test.message = 'new message' - assert(test() === 'new message') - assert(test.go() === 'new message') - assert(test[CALL]() === 'new message') - assert(test[Callable.CALL]() === 'new message') - test.go = () => 'different' - assert(test.go() === 'different') - assert(test[CALL]() === 'different') - assert(test[Callable.CALL]() === 'different') - test[CALL] = () => 'called' - assert(test.go() === 'different') - assert(test[CALL]() === 'called') - assert(test[Callable.CALL]() === 'called') - assert(test.message === 'new message') - }); - it('has own string tag Redefined', function () { - assert(Object.prototype.toString.call(new MyTestWithCall('test')) === '[object Redefined]') - }) - it("supports property redefine", function () { - const obj = new MyTestWithCall('testing') - assert(obj() === obj.go()); - obj.go = () => { - return null - } - assert(obj() === null) - assert(obj.go() === null) - obj.go = function () { - return MyTestWithCall - } - assert(obj() === MyTestWithCall) - assert(obj.go() === MyTestWithCall) - obj[CALL] = function () { - return 'check' - } - assert(obj() === 'check') - assert(obj.go() === MyTestWithCall) - }); - it('has only one [Callable.CALL] method', function () { - assert(MyTestWithCall[CALL] === MyTestWithCall[Callable.CALL] && Callable.CALL === CALL) - }) -}); diff --git a/src/test/test.cjs b/src/test/test.cjs new file mode 100644 index 0000000..16372d0 --- /dev/null +++ b/src/test/test.cjs @@ -0,0 +1,216 @@ +var assert = require("assert"); +var _ = require("callable-instance"); +var Callable = _.default +var CALL = _.CALL + +function getTitle(Class, isDefault) { + return `${Class.name}${isDefault ? ' default' : ''} (cjs)` +} + +describe(getTitle(Callable) + ' Callable Class Test', function () { + it('correctly inherits prototypes', function () { + assert(new Callable() instanceof Object) + assert(new Callable() instanceof Function) + assert(new Callable() instanceof Callable) + }) + it("Callable.CALL symbol is equal to CALL symbol", function () { + assert(Callable.CALL === CALL) + }) +}) + +function defaultTest(Class, instances = []) { + return describe(getTitle(Class, true), function () { + it('is callable', function () { + assert(typeof new Class('msg') === 'function') + assert((function () { + let test + try { + test = new Class('msg')() + } catch (e) { + return false + } + return true + })()) + }) + it('correctly inherits prototypes', function () { + + assert(typeof new Class('msg') === 'function') + const InstancesDefault = [Object, Function, Callable, Class] + for (let i = 0; i !== InstancesDefault.length; i++) { + assert(new Class('msg') instanceof InstancesDefault[i]) + } + for (let i = 0; i !== instances.length; i++) { + assert(new Class('msg') instanceof InstancesDefault[i]) + } + + }) + it('copies name property from constructor', function () { + assert(new Class('test').name === Class.name) + }) + it("has length property set to 0 due to ...args", function () { + assert(new Class("testing").length === 0); + }); + }) +} + + + +class MyTest extends Callable { + constructor(message) { + super("go"); + this.message = message; + } + + go(arg) { + return arg || this.message; + } +} + +defaultTest(MyTest) + +describe(getTitle(MyTest), function () { + it('has same return as go method', function () { + assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) + }); + it("correctly bounds this", function () { + const test = new MyTest('testing') + assert(test() === 'testing') + assert(test.go() === 'testing') + test.message = 'new message' + assert(test() === 'new message') + assert(test.go() === 'new message') + }); + it('has own string tag Callable', function () { + assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') + }) + it("supports property redefine", function () { + const obj = new MyTest('testing') + assert(obj() === obj.go()); + obj.go = () => { + return null + } + assert(obj() === null) + assert(obj.go() === null) + obj.go = function () { + return MyTest + } + assert(obj() === MyTest) + assert(obj.go() === MyTest) + }); +}); + +class MyTestExtended extends MyTest { + constructor(msg) { + super() + this.message = msg + } + go() { + return this.message + } +} + +defaultTest(MyTestExtended, [MyTest]) + +describe(getTitle(MyTestExtended), function () { + it('has same return as go method', function () { + assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) + }); + it("correctly bounds this", function () { + const test = new MyTest('testing') + assert(test() === 'testing') + assert(test.go() === 'testing') + test.message = 'new message' + assert(test() === 'new message') + assert(test.go() === 'new message') + }); + it('has own string tag Callable', function () { + assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') + }) + it("supports property redefine", function () { + const obj = new MyTestExtended('testing') + assert(obj() === obj.go()); + obj.go = () => { + return null + } + assert(obj() === null) + assert(obj.go() === null) + obj.go = function () { + return MyTestExtended + } + assert(obj() === MyTestExtended) + assert(obj.go() === MyTestExtended) + }); +}) + +class MyTestWithCall extends Callable { + constructor(message) { + super(); + this.message = message; + } + get [Symbol.toStringTag]() { + return 'Redefined' + } + go(arg) { + return arg || this.message; + } + [Callable.CALL](arg) { + return this.go(arg) + } + [CALL](arg) { + return this.go(arg) + } +} + +defaultTest(MyTestWithCall) + +describe(getTitle(MyTestWithCall), function () { + it('has same return as go method', function () { + assert(new MyTestWithCall('test').go('arg') === new MyTest('test')('arg')) + }); + it("correctly bounds this", function () { + const test = new MyTestWithCall('testing') + assert(test() === 'testing') + assert(test.go() === 'testing') + assert(test[CALL]() === 'testing') + assert(test[Callable.CALL]() === 'testing') + test.message = 'new message' + assert(test() === 'new message') + assert(test.go() === 'new message') + assert(test[CALL]() === 'new message') + assert(test[Callable.CALL]() === 'new message') + test.go = () => 'different' + assert(test.go() === 'different') + assert(test[CALL]() === 'different') + assert(test[Callable.CALL]() === 'different') + test[CALL] = () => 'called' + assert(test.go() === 'different') + assert(test[CALL]() === 'called') + assert(test[Callable.CALL]() === 'called') + assert(test.message === 'new message') + }); + it('has own string tag Redefined', function () { + assert(Object.prototype.toString.call(new MyTestWithCall('test')) === '[object Redefined]') + }) + it("supports property redefine", function () { + const obj = new MyTestWithCall('testing') + assert(obj() === obj.go()); + obj.go = () => { + return null + } + assert(obj() === null) + assert(obj.go() === null) + obj.go = function () { + return MyTestWithCall + } + assert(obj() === MyTestWithCall) + assert(obj.go() === MyTestWithCall) + obj[CALL] = function () { + return 'check' + } + assert(obj() === 'check') + assert(obj.go() === MyTestWithCall) + }); + it('has only one [Callable.CALL] method', function () { + assert(MyTestWithCall[CALL] === MyTestWithCall[Callable.CALL] && Callable.CALL === CALL) + }) +}); diff --git a/src/test/esmodule.mjs b/src/test/test.mjs similarity index 98% rename from src/test/esmodule.mjs rename to src/test/test.mjs index 7bbe793..186d188 100644 --- a/src/test/esmodule.mjs +++ b/src/test/test.mjs @@ -1,7 +1,5 @@ import assert from "assert"; -import C, { CALL } from '../../dist/index.js' - -const Callable = C.default +import Callable, { CALL } from "callable-instance" function getTitle(Class, isDefault) { return `${Class.name}${isDefault ? ' default' : ''} (mjs)` diff --git a/src/test/typeCheck.ts b/src/test/typeCheck.ts index f5ef486..c0274f1 100644 --- a/src/test/typeCheck.ts +++ b/src/test/typeCheck.ts @@ -1,8 +1,7 @@ import { expectType } from "ts-expect"; +import Callable, { CALL, OverrideCall } from "callable-instance"; -import Callable, { CALL, RedefineCall } from "callable-instance"; - -class RepeaterWithFuncGeneric extends Callable<(x: string) => string, 'go'> { +class RepeaterWithFuncGeneric extends Callable<(x: string) => string, 'go'>{ constructor(public count: number) { super('go'); } @@ -56,7 +55,7 @@ class RepeaterWithCALL extends Callable { } } -class RepeaterWithTest extends Callable<(arg: 'test') => string> { +class RepeaterWithTest extends Callable<{ (arg: 'test'): string, (arg: number): number, ttt: 't' }> { constructor(public count: number) { super(); } @@ -73,7 +72,8 @@ class RepeaterWithTest extends Callable<(arg: 'test') => string> { const test23 = new RepeaterWithTest(23) const ggg = test23('test') - +test23(23) +test23.ttt const test = new RepeaterWithCALL(4) test.go('23') // const TTTTTT = test.CALL @@ -198,6 +198,7 @@ class TestCallable extends Callable{ constructor() { super('test') } + static readonly t = 'ttt' test() { return 'test' @@ -206,15 +207,31 @@ class TestCallable extends Callable{ const testst = new TestCallable() -class TestCallableExtends extends (TestCallable as RedefineCall){ +class TestCallableExtends extends (TestCallable as OverrideCall){ constructor() { super() } - test() { + go34(){ return 23 } + test() { + return '23' + } +} + +class TestCallableExtendsS extends (TestCallable as OverrideCall){ + constructor() { + super() + } + test() { + return 'test' + } } +const tttttttt = TestCallableExtends.t +const ttttttt2t = TestCallableExtendsS.t +// TestCallableExtendsS.t = 2 + const newewe = new TestCallableExtends() newewe.test() const ttt = newewe() \ No newline at end of file diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json new file mode 100644 index 0000000..576b1c0 --- /dev/null +++ b/tsconfig-cjs.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist/cjs", + "target": "es6", + }, +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index c592425..53ee81a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,27 +1,34 @@ { "compilerOptions": { - "outDir": "./dist", + /// for cjs - tsconfig-cjs.json /// + "module": "es6", + "outDir": "dist/mjs", "target": "es6", + /// for cjs - tsconfig-cjs.json /// "allowJs": true, - "module": "NodeNext", - "moduleResolution": "NodeNext", - "esModuleInterop": true, - "noImplicitAny": false, - "removeComments": true, + "allowSyntheticDefaultImports": true, + "baseUrl": "src", "declaration": false, - "sourceMap": true, + "esModuleInterop": true, + "inlineSourceMap": false, + "listEmittedFiles": false, + "listFiles": false, + "moduleResolution": "node", + "pretty": true, + "rootDir": "src", + "skipLibCheck": true, + "strict": true, + "traceResolution": false, "typeRoots": [ - "node_modules/@types", "src/types" + "node_modules/@types", "lib" ] }, - "include": [ - "src" - ], + "include": [ "src" ], "exclude": [ "test", "src/types", "src/test", "lib", - "node_modules" + "node_modules", ] } \ No newline at end of file From bd47956a2f5bd01ade05d2019e80c5c908cfb370 Mon Sep 17 00:00:00 2001 From: pabadm Date: Tue, 26 Sep 2023 00:59:48 +0200 Subject: [PATCH 05/60] removed comments from build --- dist/cjs/index.js | 10 +++++----- dist/mjs/index.js | 10 +++++----- tsconfig.json | 1 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/dist/cjs/index.js b/dist/cjs/index.js index 3f64f5e..77a5223 100644 --- a/dist/cjs/index.js +++ b/dist/cjs/index.js @@ -1,8 +1,8 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CALL = void 0; -exports.CALL = Symbol("Callable.CALL"); // CALL symbol -const BOUND = Symbol("Callable.BOUND"); // unique symbol for binding function +exports.CALL = Symbol("Callable.CALL"); +const BOUND = Symbol("Callable.BOUND"); class Callable extends Function { static get CALL() { return exports.CALL; @@ -11,14 +11,14 @@ class Callable extends Function { return "Callable"; } constructor(property = exports.CALL) { - super("...a", "return this.a[this.b][this.c](...a)"); // calls property from child class ( function (...args)){ return this[BOUND][property](...args)}) + super("...a", "return this.a[this.b][this.c](...a)"); this[BOUND] = this.bind({ a: this, b: BOUND, c: property }); - Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); // copies constructor name - return this[BOUND]; // returns Bound function + Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); + return this[BOUND]; } } exports.default = Callable; diff --git a/dist/mjs/index.js b/dist/mjs/index.js index 10d9592..3cc1ec7 100644 --- a/dist/mjs/index.js +++ b/dist/mjs/index.js @@ -1,5 +1,5 @@ -export const CALL = Symbol("Callable.CALL"); // CALL symbol -const BOUND = Symbol("Callable.BOUND"); // unique symbol for binding function +export const CALL = Symbol("Callable.CALL"); +const BOUND = Symbol("Callable.BOUND"); class Callable extends Function { static get CALL() { return CALL; @@ -8,14 +8,14 @@ class Callable extends Function { return "Callable"; } constructor(property = CALL) { - super("...a", "return this.a[this.b][this.c](...a)"); // calls property from child class ( function (...args)){ return this[BOUND][property](...args)}) + super("...a", "return this.a[this.b][this.c](...a)"); this[BOUND] = this.bind({ a: this, b: BOUND, c: property }); - Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); // copies constructor name - return this[BOUND]; // returns Bound function + Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); + return this[BOUND]; } } export default Callable; diff --git a/tsconfig.json b/tsconfig.json index 53ee81a..a3a4a23 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "outDir": "dist/mjs", "target": "es6", /// for cjs - tsconfig-cjs.json /// + "removeComments": true, "allowJs": true, "allowSyntheticDefaultImports": true, "baseUrl": "src", From 704ec376d778c5fc97019eecced33f91e13f4e14 Mon Sep 17 00:00:00 2001 From: pabadm Date: Thu, 28 Sep 2023 21:11:15 +0200 Subject: [PATCH 06/60] nojs.yml and support for both ES6 modules and CJS --- .github/workflows/node.js.yml | 2 +- .gitignore | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 2a441af..037750a 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [14.x, 16.x, 18.x, 19.x] + node-version: [16.x, 18.x, 19.x, 20.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: diff --git a/.gitignore b/.gitignore index e9a3d6e..eeea8ba 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,7 @@ jspm_packages .npm # Optional REPL history -.node_repl_history \ No newline at end of file +.node_repl_history + +# dist +dist \ No newline at end of file From 8eb3da2375439439636ca30f2f2d8e958c3315fc Mon Sep 17 00:00:00 2001 From: pabadm Date: Wed, 25 Oct 2023 10:09:49 +0200 Subject: [PATCH 07/60] typescrip tests --- .github/workflows/node.js.yml | 2 +- dist/cjs/index.js | 24 --- dist/cjs/package.json | 3 - dist/mjs/index.js | 21 --- dist/mjs/package.json | 3 - lib/index.d.ts | 112 +++++++----- src/index.js | 38 ++-- src/test/classGeneric.ts | 122 +++++++++++++ src/test/funcGeneric.ts | 133 ++++++++++++++ src/test/interfaceGeneric.ts | 161 +++++++++++++++++ src/test/overrideCallProperties.ts | 69 +++++++ src/test/test.cjs | 278 +++++++++++++++-------------- src/test/test.mjs | 276 ++++++++++++++-------------- src/test/typeCheck.ts | 237 ------------------------ 14 files changed, 865 insertions(+), 614 deletions(-) delete mode 100644 dist/cjs/index.js delete mode 100644 dist/cjs/package.json delete mode 100644 dist/mjs/index.js delete mode 100644 dist/mjs/package.json create mode 100644 src/test/classGeneric.ts create mode 100644 src/test/funcGeneric.ts create mode 100644 src/test/interfaceGeneric.ts create mode 100644 src/test/overrideCallProperties.ts delete mode 100644 src/test/typeCheck.ts diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 037750a..e176589 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x, 19.x, 20.x] + node-version: [16.x, 18.x, 19.x, 20.x, 21.x, 22.x, 23.x, 24.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: diff --git a/dist/cjs/index.js b/dist/cjs/index.js deleted file mode 100644 index 77a5223..0000000 --- a/dist/cjs/index.js +++ /dev/null @@ -1,24 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CALL = void 0; -exports.CALL = Symbol("Callable.CALL"); -const BOUND = Symbol("Callable.BOUND"); -class Callable extends Function { - static get CALL() { - return exports.CALL; - } - get [Symbol.toStringTag]() { - return "Callable"; - } - constructor(property = exports.CALL) { - super("...a", "return this.a[this.b][this.c](...a)"); - this[BOUND] = this.bind({ - a: this, - b: BOUND, - c: property - }); - Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); - return this[BOUND]; - } -} -exports.default = Callable; diff --git a/dist/cjs/package.json b/dist/cjs/package.json deleted file mode 100644 index 1cd945a..0000000 --- a/dist/cjs/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "commonjs" -} diff --git a/dist/mjs/index.js b/dist/mjs/index.js deleted file mode 100644 index 3cc1ec7..0000000 --- a/dist/mjs/index.js +++ /dev/null @@ -1,21 +0,0 @@ -export const CALL = Symbol("Callable.CALL"); -const BOUND = Symbol("Callable.BOUND"); -class Callable extends Function { - static get CALL() { - return CALL; - } - get [Symbol.toStringTag]() { - return "Callable"; - } - constructor(property = CALL) { - super("...a", "return this.a[this.b][this.c](...a)"); - this[BOUND] = this.bind({ - a: this, - b: BOUND, - c: property - }); - Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); - return this[BOUND]; - } -} -export default Callable; diff --git a/dist/mjs/package.json b/dist/mjs/package.json deleted file mode 100644 index 4720025..0000000 --- a/dist/mjs/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "module" -} diff --git a/lib/index.d.ts b/lib/index.d.ts index 7358395..7488f24 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,42 +1,70 @@ -declare module 'callable-instance' { - export const CALL: unique symbol; - export type SCALL = typeof CALL - - type BaseProperty = symbol | string | number; - type CustomProperty = Exclude; - - type BaseFunc = (...args: any) => any; - type BaseClass = abstract new (...args: any) => any; - type BaseInterface = { - [k: BaseProperty]: any - }; - - type ExtractFuncFromInterface = I[P] extends BaseFunc ? I[P] : never; - - // to avoid error (recursively references itself as a base type) - interface CloneFuncFromClass { - (...args: Parameters[P]>): ReturnType[P]>; - } - - type ExtractFunc = C extends BaseClass - ? CloneFuncFromClass - : C extends BaseFunc - ? C - : C extends BaseInterface - ? ExtractFuncFromInterface - : never - - export interface CallableConstructor { - readonly CALL: SCALL; - new (property: P): ExtractFunc; - new (): ExtractFunc; - } - - export type OverrideCall = { - new (...args: ConstructorParameters): - Pick, BaseProperty> & ExtractFunc - } & Omit - - const Callable: CallableConstructor; - export default Callable; -} \ No newline at end of file +declare module "callable-instance" { + export const CALL: unique symbol; + export type SCALL = typeof CALL; + + type BaseProperty = symbol | string | number; + type CustomProperty = Exclude; + + type BaseFunc = (...args: any) => any; + type BaseClass = abstract new (...args: any) => any; + type BaseInterface = { + [k: BaseProperty]: any; + }; + + type ExtractFuncFromInterface< + I extends BaseInterface, + P extends BaseProperty + > = I[P] extends BaseFunc ? I[P] : never; + + interface CloneFuncFromClass { + /** + * For TS generics and function override support use interface or function type for Callable + */ + (...args: Parameters[P]>): ReturnType[P]>; + } + + type ExtractFunc< + C extends BaseClass | BaseFunc | BaseInterface, + P extends BaseProperty + > = C extends BaseClass + ? CloneFuncFromClass + : C extends BaseFunc + ? C + : C extends BaseInterface + ? ExtractFuncFromInterface + : never; + + export interface CallableConstructor { + readonly CALL: SCALL; + + new < + C extends BaseClass | BaseFunc | BaseInterface, + P extends CustomProperty + >( + property: P + ): ExtractFunc; + + new (): ExtractFunc< + C, + SCALL + >; + } + + // pick for omitting call signature + type PickProperties> = { + [k in keyof Obj]: Obj[k]; + }; + + export type OverrideCall = { + new < + C extends BaseClass | BaseFunc | BaseInterface, + P extends BaseProperty = SCALL + >( + ...args: ConstructorParameters + ): Omit>, P> & ExtractFunc; + } & S; + + const Callable: CallableConstructor; + + export default Callable; +} diff --git a/src/index.js b/src/index.js index 0adaf6b..d939c25 100644 --- a/src/index.js +++ b/src/index.js @@ -1,22 +1,32 @@ -export const CALL = Symbol("Callable.CALL") // CALL symbol -const BOUND = Symbol("Callable.BOUND") // unique symbol for binding function +export const CALL = Symbol("Callable.CALL"); // CALL symbol +const BOUND = Symbol("Callable.BOUND"); // unique symbol for binding function class Callable extends Function { - static get CALL() { // getter for CALL symbol - return CALL + static get CALL() { + // getter for CALL symbol + return CALL; } - get [Symbol.toStringTag]() { // creates stringTag (Object.prototype.toString.call(new Callable()) === [object Callable]) - return "Callable" + get [Symbol.toStringTag]() { + // creates stringTag (Object.prototype.toString.call(new Callable()) === [object Callable]) + return "Callable"; } - constructor(property = CALL) { - super("...a", "return this.a[this.b][this.c](...a)") // calls property from child class ( function (...args)){ return this[BOUND][property](...args)}) - this[BOUND] = this.bind({ // binds to property, bound symbol and this + constructor(property) { + super("...a", "return this.a[this.b][this.c](...a)"); // calls func assigned to property from class that extends Callable + if (property === undefined) { + property = CALL; + } + this[BOUND] = this.bind({ + // binds to property, bound symbol and this a: this, b: BOUND, - c: property - }) - Object.defineProperty(this[BOUND], "name", Object.getOwnPropertyDescriptor(this.constructor, "name")); // copies constructor name - return this[BOUND] // returns Bound function + c: property, + }); + Object.defineProperty( + this[BOUND], + "name", + Object.getOwnPropertyDescriptor(this.constructor, "name") + ); // copies constructor name + return this[BOUND]; } } -export default Callable \ No newline at end of file +export default Callable; diff --git a/src/test/classGeneric.ts b/src/test/classGeneric.ts new file mode 100644 index 0000000..3dbd85d --- /dev/null +++ b/src/test/classGeneric.ts @@ -0,0 +1,122 @@ +import { expectType } from "ts-expect"; +import Callable, { CALL, CallableConstructor, OverrideCall } from "callable-instance"; + +// TESTS FOR CLASS-TYPE GENERICS + +class RepeaterWithClassGeneric extends Callable{ + constructor(public count: number) { + super('go'); + } + + go(arg: string): string { + return arg.repeat(this.count); + } +} + +describe("Callable With Class Generic and custom property (TypeScript)", function () { + + it("is callable", function () { + expectType<(x: string) => string>(new RepeaterWithClassGeneric(1)); + // @ts-expect-error wrong type for constructor + new RepeaterWithClassGeneric("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithClassGeneric(5).go(5); + // Valid propert access. + new RepeaterWithClassGeneric(5).count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithClassGeneric(5)); + expectType<(x: string) => string>(new RepeaterWithClassGeneric(5)); + expectType<(x: string) => string>(new RepeaterWithClassGeneric(5).go); + }); + + it("is an instance of Repeater", function () { + expectType(new RepeaterWithClassGeneric(5)); + expectType>(new RepeaterWithClassGeneric(5)); + expectType(new RepeaterWithClassGeneric(5)); + expectType(new RepeaterWithClassGeneric(5)); + }); +}); + + +// class RepeaterWithTSClassOverride extends Callable { +// constructor(public count: number) { +// super('go'); +// } +// go(arg: string): string +// go(arg: number): number +// go(arg: string | number): string | number { +// return arg +// // return arg.repeat(this.count); +// } +// } + +//// override is not supported in class generic + +// describe("Callable With TS Class Override Generic and custom property (TypeScript)", function () { +// it("is callable", function () { +// expectType<{ (x: string): string, (x: number): number }>(new RepeaterWithTSClassOverride(1)); +// // @ts-expect-error wrong type for constructor +// new RepeaterWithTSClassOverride("testing"); +// new RepeaterWithTSClassOverride(5).go(5); +// // Valid propert access. +// new RepeaterWithTSClassOverride(5).count = 4; +// }); + +// it("is an object", function () { +// expectType(new RepeaterWithTSClassOverride(5)); +// expectType<(x: string) => string>(new RepeaterWithTSClassOverride(5).go); +// expectType<{ (x: string): string, (x: number): number }>(new RepeaterWithTSClassOverride(5)) +// expectType<(x: string) => string>(() => new RepeaterWithTSClassOverride(5)('23')) +// expectType<(x: number) => number>(() => new RepeaterWithTSClassOverride(5)(23)) + +// }); + +// it("is an instance of Repeater", function () { +// expectType(new RepeaterWithTSClassOverride(5)); +// expectType>(new RepeaterWithTSClassOverride(5)); +// expectType(new RepeaterWithTSClassOverride(5)); +// expectType(new RepeaterWithTSClassOverride(5)); +// }); +// }); + + + +class RepeaterWithOverridenFunc extends (RepeaterWithClassGeneric as OverrideCall){ + constructor() { + super(23) + } + go() { + return 23 + } +} + +describe("Callable With Func Overriding Generic and custom property (TypeScript)", function () { + it("is callable", function () { + expectType<() => number>(new RepeaterWithOverridenFunc()); + // @ts-expect-error wrong type for constructor + new RepeaterWithOverridenFunc()("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithOverridenFunc()(5).go(5); + // Valid propert access. + new RepeaterWithOverridenFunc().count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithOverridenFunc()); + expectType<() => number>(new RepeaterWithOverridenFunc().go); + expectType<() => number>(new RepeaterWithOverridenFunc()) + expectType<(x: number) => number>(new RepeaterWithOverridenFunc().go) + + }); + + it("is an instance of Repeater", function () { + // is not passed because for typescript OverrideCall is other class + // expectType(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithOverridenFunc()); + expectType>(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithOverridenFunc()); + }); +}); \ No newline at end of file diff --git a/src/test/funcGeneric.ts b/src/test/funcGeneric.ts new file mode 100644 index 0000000..e2ca7c4 --- /dev/null +++ b/src/test/funcGeneric.ts @@ -0,0 +1,133 @@ +import { expectType } from "ts-expect"; +import Callable, { CallableConstructor, OverrideCall } from "callable-instance"; + +// TESTS FOR FUNCTION-TYPE GENERICS +class RepeaterWithFuncGeneric extends Callable<(x: string)=> string, "go"> { + constructor(public count: number) { + super("go"); + } + + go(arg: string): string { + return arg.repeat(this.count); + } +} + +describe("Callable With Func Generic and custom property (TypeScript)", function () { + it("is callable", function () { + expectType<(x: string) => string>(new RepeaterWithFuncGeneric(1)); + // @ts-expect-error wrong type for constructor + new RepeaterWithFuncGeneric("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithFuncGeneric(5).go(5); + // Valid propert access. + new RepeaterWithFuncGeneric(5).count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithFuncGeneric(5)); + expectType<(x: string) => string>(new RepeaterWithFuncGeneric(5)); + expectType<(x: string) => string>(new RepeaterWithFuncGeneric(5).go); + }); + + it("is an instance of Repeater", function () { + expectType(new RepeaterWithFuncGeneric(5)); + expectType>( + new RepeaterWithFuncGeneric(5) + ); + expectType(new RepeaterWithFuncGeneric(5)); + expectType(new RepeaterWithFuncGeneric(5)); + }); +}); + +interface FuncOverride { + (x: number): number; + (x: string): string; +} + +class RepeaterWithTSFuncOverride extends Callable { + constructor(public count: number) { + super("go"); + } + + go(arg: string): string { + return arg.repeat(this.count); + } +} + +describe("Callable With Func Overriding Generic and custom property (TypeScript)", function () { + it("is callable", function () { + expectType<{ (x: string): string; (x: number): number }>( + new RepeaterWithTSFuncOverride(1) + ); + // @ts-expect-error wrong type for constructor + new RepeaterWithTSFuncOverride("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithTSFuncOverride(5).go(5); + // Valid propert access. + new RepeaterWithTSFuncOverride(5).count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithTSFuncOverride(5)); + expectType<(x: string) => string>(new RepeaterWithTSFuncOverride(5).go); + expectType<{ (x: string): string; (x: number): number }>( + new RepeaterWithTSFuncOverride(5) + ); + expectType<(x: string) => string>(() => + new RepeaterWithTSFuncOverride(5)("23") + ); + expectType<(x: number) => number>(() => + new RepeaterWithTSFuncOverride(5)(23) + ); + }); + + it("is an instance of Repeater", function () { + expectType(new RepeaterWithTSFuncOverride(5)); + expectType>( + new RepeaterWithTSFuncOverride(5) + ); + expectType(new RepeaterWithTSFuncOverride(5)); + expectType(new RepeaterWithTSFuncOverride(5)); + }); +}); + +class RepeaterWithOverridenFunc extends (RepeaterWithFuncGeneric as OverrideCall< + typeof RepeaterWithFuncGeneric +>)<() => number, "go"> { + constructor() { + super(23); + } + go() { + return 23; + } +} + +describe("Callable With TS Func Override Generic and custom property (TypeScript)", function () { + it("is callable", function () { + expectType<() => number>(new RepeaterWithOverridenFunc()); + // @ts-expect-error wrong type for constructor + new RepeaterWithOverridenFunc()("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithOverridenFunc()(5).go(5); + // Valid propert access. + new RepeaterWithOverridenFunc().count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithOverridenFunc()); + expectType<() => number>(new RepeaterWithOverridenFunc().go); + expectType<() => number>(new RepeaterWithOverridenFunc()); + expectType<(x: number) => number>(new RepeaterWithOverridenFunc().go); + }); + + it("is an instance of Repeater", function () { + // is not passed because for typescript OverrideCall is other class + // expectType(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithOverridenFunc()); + expectType>( + new RepeaterWithOverridenFunc() + ); + expectType(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithOverridenFunc()); + }); +}); diff --git a/src/test/interfaceGeneric.ts b/src/test/interfaceGeneric.ts new file mode 100644 index 0000000..471ced9 --- /dev/null +++ b/src/test/interfaceGeneric.ts @@ -0,0 +1,161 @@ +import { expectType } from "ts-expect"; +import Callable, { CallableConstructor, OverrideCall } from "callable-instance"; + +// TESTS FOR INTERFACE-TYPE GENERICS +interface IRepeaterWithInterfaceGeneric { + go: (x: string) => string; +} + +class RepeaterWithInterfaceGeneric + extends Callable + implements IRepeaterWithInterfaceGeneric +{ + constructor(public count: number) { + super("go"); + } + + go(arg: string): string { + return arg.repeat(this.count); + } +} + +describe("Callable With Interface Generic and custom property (TypeScript)", function () { + it("is callable", function () { + expectType<(x: string) => string>(new RepeaterWithInterfaceGeneric(1)); + // @ts-expect-error wrong type for constructor + new RepeaterWithInterfaceGeneric("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithInterfaceGeneric(5).go(5); + // Valid propert access. + new RepeaterWithInterfaceGeneric(5).count = 4; + }); + + it("is an object", function () { + expectType( + new RepeaterWithInterfaceGeneric(5) + ); + expectType<(x: string) => string>(new RepeaterWithInterfaceGeneric(5)); + expectType<(x: string) => string>(new RepeaterWithInterfaceGeneric(5).go); + }); + + it("is an instance of Repeater", function () { + expectType( + new RepeaterWithInterfaceGeneric(5) + ); + expectType>( + new RepeaterWithInterfaceGeneric(5) + ); + expectType(new RepeaterWithInterfaceGeneric(5)); + expectType(new RepeaterWithInterfaceGeneric(5)); + }); +}); + +interface FuncOverride { + go(x: number): number; + go(x: string): string; +} + +class RepeaterWithTSInterfaceOverride + extends Callable + implements FuncOverride +{ + constructor(public count: number) { + super("go"); + } + go(arg: number): number; + go(arg: string): string; + go(arg: string | number): string | number { + return arg; + // return arg.repeat(this.count); + } +} + +describe("Callable With TS Interface Override Generic and custom property (TypeScript)", function () { + it("is callable", function () { + expectType<{ (x: string): string; (x: number): number }>( + new RepeaterWithTSInterfaceOverride(1) + ); + // @ts-expect-error wrong type for constructor + new RepeaterWithTSInterfaceOverride("testing"); + new RepeaterWithTSInterfaceOverride(5).go(5); + // Valid propert access. + new RepeaterWithTSInterfaceOverride(5).count = 4; + }); + + it("is an object", function () { + expectType( + new RepeaterWithTSInterfaceOverride(5) + ); + expectType<(x: string) => string>( + new RepeaterWithTSInterfaceOverride(5).go + ); + expectType<{ (x: string): string; (x: number): number }>( + new RepeaterWithTSInterfaceOverride(5) + ); + expectType<(x: string) => string>(() => + new RepeaterWithTSInterfaceOverride(5)("23") + ); + expectType<(x: number) => number>(() => + new RepeaterWithTSInterfaceOverride(5)(23) + ); + }); + + it("is an instance of Repeater", function () { + expectType( + new RepeaterWithTSInterfaceOverride(5) + ); + expectType>( + new RepeaterWithTSInterfaceOverride(5) + ); + expectType(new RepeaterWithTSInterfaceOverride(5)); + expectType(new RepeaterWithTSInterfaceOverride(5)); + }); +}); + +interface OverridenType { + go(): number; +} + +class RepeaterWithOverridenFunc + extends (RepeaterWithInterfaceGeneric as OverrideCall< + typeof RepeaterWithInterfaceGeneric + >) + implements OverridenType +{ + constructor() { + super(23); + } + go() { + return 23; + } +} + +describe("Callable With Func Overriding Generic and custom property (TypeScript)", function () { + it("is callable", function () { + expectType<() => number>(new RepeaterWithOverridenFunc()); + // @ts-expect-error wrong type for constructor + new RepeaterWithOverridenFunc()("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithOverridenFunc()(5).go(5); + // Valid propert access. + new RepeaterWithOverridenFunc().count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithOverridenFunc()); + expectType<() => number>(new RepeaterWithOverridenFunc().go); + expectType<() => number>(new RepeaterWithOverridenFunc()); + expectType<(x: number) => number>(new RepeaterWithOverridenFunc().go); + }); + + it("is an instance of Repeater", function () { + // is not passed because for typescript OverrideCall is other class + // expectType(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithOverridenFunc()); + expectType>( + new RepeaterWithOverridenFunc() + ); + expectType(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithOverridenFunc()); + }); +}); diff --git a/src/test/overrideCallProperties.ts b/src/test/overrideCallProperties.ts new file mode 100644 index 0000000..d464b00 --- /dev/null +++ b/src/test/overrideCallProperties.ts @@ -0,0 +1,69 @@ +import { expectType } from "ts-expect"; +import Callable, { + CALL, + CallableConstructor, + OverrideCall, +} from "callable-instance"; + +class MyClass extends Callable { + constructor() { + super(); + } + static staticProperty = "static"; + static readonly staticReadonlyProperty = "staticReadonly" as const; + public publicProperty = "public"; + public readonly readonlyProperty = "readonly" as const; + + private [CALL]() { + return 32; + } +} + +describe("Class that extended Callable (TypeScript)", function () { + it("has its properties", function () { + expectType(MyClass.staticProperty); + MyClass.staticProperty = "test"; + expectType<"staticReadonly">(MyClass.staticReadonlyProperty); + // MyClass.staticReadonlyProperty = 'test' + expectType(new MyClass().publicProperty); + new MyClass().publicProperty = "test"; + expectType<"readonly">(new MyClass().readonlyProperty); + // new MyClass().readonlyProperty = 'test' + }); +}); + +class Extended extends (MyClass as OverrideCall)< + typeof Extended +> { + static newStaticProperty = "newStatic"; + static readonly newStaticReadonlyProperty = "readonly" as const; + public newPublicProperty = "public"; + public readonly newReadonlyProperty = "readonly" as const; + + [CALL]() { + return "str"; + } +} + +describe("Class that extended previous (TypeScript)", function () { + it("has parent`s properties", function () { + expectType(Extended.staticProperty); + Extended.staticProperty = "test"; + expectType<"staticReadonly">(Extended.staticReadonlyProperty); + // Extended.staticReadonlyProperty = 'test' + expectType(new Extended().publicProperty); + new Extended().publicProperty = "test"; + expectType<"readonly">(new Extended().readonlyProperty); + // new Extended().readonlyProperty = 'rer' + }); + it("has it`s own properties", function () { + expectType(Extended.newStaticProperty); + Extended.newStaticProperty = "test"; + expectType<"readonly">(Extended.newStaticReadonlyProperty); + // Extended.staticReadonlyProperty = 'test' + expectType(new Extended().newPublicProperty); + new Extended().publicProperty = "test"; + expectType<"readonly">(new Extended().newReadonlyProperty); + // new Extended().readonlyProperty = 'rer' + }); +}); diff --git a/src/test/test.cjs b/src/test/test.cjs index 16372d0..5085acf 100644 --- a/src/test/test.cjs +++ b/src/test/test.cjs @@ -1,60 +1,58 @@ var assert = require("assert"); var _ = require("callable-instance"); -var Callable = _.default -var CALL = _.CALL +var Callable = _.default; +var CALL = _.CALL; function getTitle(Class, isDefault) { - return `${Class.name}${isDefault ? ' default' : ''} (cjs)` + return `${Class.name}${isDefault ? " default" : ""} (cjs)`; } -describe(getTitle(Callable) + ' Callable Class Test', function () { - it('correctly inherits prototypes', function () { - assert(new Callable() instanceof Object) - assert(new Callable() instanceof Function) - assert(new Callable() instanceof Callable) - }) +describe(getTitle(Callable) + " Callable Class Test", function () { + it("correctly inherits prototypes", function () { + assert(new Callable() instanceof Object); + assert(new Callable() instanceof Function); + assert(new Callable() instanceof Callable); + }); it("Callable.CALL symbol is equal to CALL symbol", function () { - assert(Callable.CALL === CALL) - }) -}) + assert(Callable.CALL === CALL); + }); +}); -function defaultTest(Class, instances = []) { +function defaultTest(Class, prototypes = []) { return describe(getTitle(Class, true), function () { - it('is callable', function () { - assert(typeof new Class('msg') === 'function') - assert((function () { - let test - try { - test = new Class('msg')() - } catch (e) { - return false - } - return true - })()) - }) - it('correctly inherits prototypes', function () { - - assert(typeof new Class('msg') === 'function') - const InstancesDefault = [Object, Function, Callable, Class] - for (let i = 0; i !== InstancesDefault.length; i++) { - assert(new Class('msg') instanceof InstancesDefault[i]) + it("is callable", function () { + assert(typeof new Class("msg") === "function"); + assert( + (function () { + let test; + try { + test = new Class("msg")(); + } catch (e) { + return false; + } + return true; + })() + ); + }); + it("correctly inherits prototypes", function () { + assert(typeof new Class("msg") === "function"); + const defaultPrototypes = [Object, Function, Callable, Class]; + for (let i = 0; i !== defaultPrototypes.length; i++) { + assert(new Class("msg") instanceof defaultPrototypes[i]); } - for (let i = 0; i !== instances.length; i++) { - assert(new Class('msg') instanceof InstancesDefault[i]) + for (let i = 0; i !== prototypes.length; i++) { + assert(new Class("msg") instanceof defaultPrototypes[i]); } - - }) - it('copies name property from constructor', function () { - assert(new Class('test').name === Class.name) - }) + }); + it("copies name property from constructor", function () { + assert(new Class("test").name === Class.name); + }); it("has length property set to 0 due to ...args", function () { assert(new Class("testing").length === 0); }); - }) + }); } - - class MyTest extends Callable { constructor(message) { super("go"); @@ -66,81 +64,85 @@ class MyTest extends Callable { } } -defaultTest(MyTest) +defaultTest(MyTest); describe(getTitle(MyTest), function () { - it('has same return as go method', function () { - assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) + it("has same return as go method", function () { + assert(new MyTest("test").go("arg") === new MyTest("test")("arg")); }); it("correctly bounds this", function () { - const test = new MyTest('testing') - assert(test() === 'testing') - assert(test.go() === 'testing') - test.message = 'new message' - assert(test() === 'new message') - assert(test.go() === 'new message') - }); - it('has own string tag Callable', function () { - assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') - }) + const test = new MyTest("testing"); + assert(test() === "testing"); + assert(test.go() === "testing"); + test.message = "new message"; + assert(test() === "new message"); + assert(test.go() === "new message"); + }); + it("has own string tag Callable", function () { + assert( + Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" + ); + }); it("supports property redefine", function () { - const obj = new MyTest('testing') + const obj = new MyTest("testing"); assert(obj() === obj.go()); obj.go = () => { - return null - } - assert(obj() === null) - assert(obj.go() === null) + return null; + }; + assert(obj() === null); + assert(obj.go() === null); obj.go = function () { - return MyTest - } - assert(obj() === MyTest) - assert(obj.go() === MyTest) + return MyTest; + }; + assert(obj() === MyTest); + assert(obj.go() === MyTest); }); }); class MyTestExtended extends MyTest { constructor(msg) { - super() - this.message = msg + super(); + this.message = msg; } go() { - return this.message + return this.message; } } -defaultTest(MyTestExtended, [MyTest]) +defaultTest(MyTestExtended, [MyTest]); describe(getTitle(MyTestExtended), function () { - it('has same return as go method', function () { - assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) + it("has same return as go method", function () { + assert(new MyTest("test").go("arg") === new MyTest("test")("arg")); }); it("correctly bounds this", function () { - const test = new MyTest('testing') - assert(test() === 'testing') - assert(test.go() === 'testing') - test.message = 'new message' - assert(test() === 'new message') - assert(test.go() === 'new message') - }); - it('has own string tag Callable', function () { - assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') - }) + const test = new MyTest("testing"); + assert(test() === "testing"); + assert(test.go() === "testing"); + test.message = "new message"; + assert(test() === "new message"); + assert(test.go() === "new message"); + }); + it("has own string tag Callable", function () { + assert( + Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" + ); + }); it("supports property redefine", function () { - const obj = new MyTestExtended('testing') + const obj = new MyTestExtended("testing"); assert(obj() === obj.go()); obj.go = () => { - return null - } - assert(obj() === null) - assert(obj.go() === null) + return null; + }; + assert(obj() === null); + assert(obj.go() === null); obj.go = function () { - return MyTestExtended - } - assert(obj() === MyTestExtended) - assert(obj.go() === MyTestExtended) + return MyTestExtended; + }; + assert(obj() === MyTestExtended); + assert(obj.go() === MyTestExtended); }); -}) +}); class MyTestWithCall extends Callable { constructor(message) { @@ -148,69 +150,75 @@ class MyTestWithCall extends Callable { this.message = message; } get [Symbol.toStringTag]() { - return 'Redefined' + return "Redefined"; } go(arg) { return arg || this.message; } [Callable.CALL](arg) { - return this.go(arg) + return this.go(arg); } [CALL](arg) { - return this.go(arg) + return this.go(arg); } } -defaultTest(MyTestWithCall) +defaultTest(MyTestWithCall); describe(getTitle(MyTestWithCall), function () { - it('has same return as go method', function () { - assert(new MyTestWithCall('test').go('arg') === new MyTest('test')('arg')) + it("has same return as go method", function () { + assert(new MyTestWithCall("test").go("arg") === new MyTest("test")("arg")); }); it("correctly bounds this", function () { - const test = new MyTestWithCall('testing') - assert(test() === 'testing') - assert(test.go() === 'testing') - assert(test[CALL]() === 'testing') - assert(test[Callable.CALL]() === 'testing') - test.message = 'new message' - assert(test() === 'new message') - assert(test.go() === 'new message') - assert(test[CALL]() === 'new message') - assert(test[Callable.CALL]() === 'new message') - test.go = () => 'different' - assert(test.go() === 'different') - assert(test[CALL]() === 'different') - assert(test[Callable.CALL]() === 'different') - test[CALL] = () => 'called' - assert(test.go() === 'different') - assert(test[CALL]() === 'called') - assert(test[Callable.CALL]() === 'called') - assert(test.message === 'new message') - }); - it('has own string tag Redefined', function () { - assert(Object.prototype.toString.call(new MyTestWithCall('test')) === '[object Redefined]') - }) + const test = new MyTestWithCall("testing"); + assert(test() === "testing"); + assert(test.go() === "testing"); + assert(test[CALL]() === "testing"); + assert(test[Callable.CALL]() === "testing"); + test.message = "new message"; + assert(test() === "new message"); + assert(test.go() === "new message"); + assert(test[CALL]() === "new message"); + assert(test[Callable.CALL]() === "new message"); + test.go = () => "different"; + assert(test.go() === "different"); + assert(test[CALL]() === "different"); + assert(test[Callable.CALL]() === "different"); + test[CALL] = () => "called"; + assert(test.go() === "different"); + assert(test[CALL]() === "called"); + assert(test[Callable.CALL]() === "called"); + assert(test.message === "new message"); + }); + it("has own string tag Redefined", function () { + assert( + Object.prototype.toString.call(new MyTestWithCall("test")) === + "[object Redefined]" + ); + }); it("supports property redefine", function () { - const obj = new MyTestWithCall('testing') + const obj = new MyTestWithCall("testing"); assert(obj() === obj.go()); obj.go = () => { - return null - } - assert(obj() === null) - assert(obj.go() === null) + return null; + }; + assert(obj() === null); + assert(obj.go() === null); obj.go = function () { - return MyTestWithCall - } - assert(obj() === MyTestWithCall) - assert(obj.go() === MyTestWithCall) + return MyTestWithCall; + }; + assert(obj() === MyTestWithCall); + assert(obj.go() === MyTestWithCall); obj[CALL] = function () { - return 'check' - } - assert(obj() === 'check') - assert(obj.go() === MyTestWithCall) - }); - it('has only one [Callable.CALL] method', function () { - assert(MyTestWithCall[CALL] === MyTestWithCall[Callable.CALL] && Callable.CALL === CALL) - }) + return "check"; + }; + assert(obj() === "check"); + assert(obj.go() === MyTestWithCall); + }); + it("has only one [Callable.CALL] method", function () { + assert( + MyTestWithCall[CALL] === MyTestWithCall[Callable.CALL] && + Callable.CALL === CALL + ); + }); }); diff --git a/src/test/test.mjs b/src/test/test.mjs index 186d188..30abf5f 100644 --- a/src/test/test.mjs +++ b/src/test/test.mjs @@ -1,58 +1,56 @@ import assert from "assert"; -import Callable, { CALL } from "callable-instance" +import Callable, { CALL } from "callable-instance"; function getTitle(Class, isDefault) { - return `${Class.name}${isDefault ? ' default' : ''} (mjs)` + return `${Class.name}${isDefault ? " default" : ""} (mjs)`; } -describe(getTitle(Callable) + ' Callable Class Test', function () { - it('correctly inherits prototypes', function () { - assert(new Callable() instanceof Object) - assert(new Callable() instanceof Function) - assert(new Callable() instanceof Callable) - }) +describe(getTitle(Callable) + " Callable Class Test", function () { + it("correctly inherits prototypes", function () { + assert(new Callable() instanceof Object); + assert(new Callable() instanceof Function); + assert(new Callable() instanceof Callable); + }); it("Callable.CALL symbol is equal to CALL symbol", function () { - assert(Callable.CALL === CALL) - }) -}) + assert(Callable.CALL === CALL); + }); +}); -function defaultTest(Class, instances = []) { +function defaultTest(Class, prototypes = []) { return describe(getTitle(Class, true), function () { - it('is callable', function () { - assert(typeof new Class('msg') === 'function') - assert((function () { - let test - try { - test = new Class('msg')() - } catch (e) { - return false - } - return true - })()) - }) - it('correctly inherits prototypes', function () { - - assert(typeof new Class('msg') === 'function') - const InstancesDefault = [Object, Function, Callable, Class] - for (let i = 0; i !== InstancesDefault.length; i++) { - assert(new Class('msg') instanceof InstancesDefault[i]) + it("is callable", function () { + assert(typeof new Class("msg") === "function"); + assert( + (function () { + let test; + try { + test = new Class("msg")(); + } catch (e) { + return false; + } + return true; + })() + ); + }); + it("correctly inherits prototypes", function () { + assert(typeof new Class("msg") === "function"); + const defaultPrototypes = [Object, Function, Callable, Class]; + for (let i = 0; i !== defaultPrototypes.length; i++) { + assert(new Class("msg") instanceof defaultPrototypes[i]); } - for (let i = 0; i !== instances.length; i++) { - assert(new Class('msg') instanceof InstancesDefault[i]) + for (let i = 0; i !== prototypes.length; i++) { + assert(new Class("msg") instanceof defaultPrototypes[i]); } - - }) - it('copies name property from constructor', function () { - assert(new Class('test').name === Class.name) - }) + }); + it("copies name property from constructor", function () { + assert(new Class("test").name === Class.name); + }); it("has length property set to 0 due to ...args", function () { assert(new Class("testing").length === 0); }); - }) + }); } - - class MyTest extends Callable { constructor(message) { super("go"); @@ -64,81 +62,85 @@ class MyTest extends Callable { } } -defaultTest(MyTest) +defaultTest(MyTest); describe(getTitle(MyTest), function () { - it('has same return as go method', function () { - assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) + it("has same return as go method", function () { + assert(new MyTest("test").go("arg") === new MyTest("test")("arg")); }); it("correctly bounds this", function () { - const test = new MyTest('testing') - assert(test() === 'testing') - assert(test.go() === 'testing') - test.message = 'new message' - assert(test() === 'new message') - assert(test.go() === 'new message') - }); - it('has own string tag Callable', function () { - assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') - }) + const test = new MyTest("testing"); + assert(test() === "testing"); + assert(test.go() === "testing"); + test.message = "new message"; + assert(test() === "new message"); + assert(test.go() === "new message"); + }); + it("has own string tag Callable", function () { + assert( + Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" + ); + }); it("supports property redefine", function () { - const obj = new MyTest('testing') + const obj = new MyTest("testing"); assert(obj() === obj.go()); obj.go = () => { - return null - } - assert(obj() === null) - assert(obj.go() === null) + return null; + }; + assert(obj() === null); + assert(obj.go() === null); obj.go = function () { - return MyTest - } - assert(obj() === MyTest) - assert(obj.go() === MyTest) + return MyTest; + }; + assert(obj() === MyTest); + assert(obj.go() === MyTest); }); }); class MyTestExtended extends MyTest { constructor(msg) { - super() - this.message = msg + super(); + this.message = msg; } go() { - return this.message + return this.message; } } -defaultTest(MyTestExtended, [MyTest]) +defaultTest(MyTestExtended, [MyTest]); describe(getTitle(MyTestExtended), function () { - it('has same return as go method', function () { - assert(new MyTest('test').go('arg') === new MyTest('test')('arg')) + it("has same return as go method", function () { + assert(new MyTest("test").go("arg") === new MyTest("test")("arg")); }); it("correctly bounds this", function () { - const test = new MyTest('testing') - assert(test() === 'testing') - assert(test.go() === 'testing') - test.message = 'new message' - assert(test() === 'new message') - assert(test.go() === 'new message') - }); - it('has own string tag Callable', function () { - assert(Object.prototype.toString.call(new MyTest('test')) === '[object Callable]') - }) + const test = new MyTest("testing"); + assert(test() === "testing"); + assert(test.go() === "testing"); + test.message = "new message"; + assert(test() === "new message"); + assert(test.go() === "new message"); + }); + it("has own string tag Callable", function () { + assert( + Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" + ); + }); it("supports property redefine", function () { - const obj = new MyTestExtended('testing') + const obj = new MyTestExtended("testing"); assert(obj() === obj.go()); obj.go = () => { - return null - } - assert(obj() === null) - assert(obj.go() === null) + return null; + }; + assert(obj() === null); + assert(obj.go() === null); obj.go = function () { - return MyTestExtended - } - assert(obj() === MyTestExtended) - assert(obj.go() === MyTestExtended) + return MyTestExtended; + }; + assert(obj() === MyTestExtended); + assert(obj.go() === MyTestExtended); }); -}) +}); class MyTestWithCall extends Callable { constructor(message) { @@ -146,69 +148,75 @@ class MyTestWithCall extends Callable { this.message = message; } get [Symbol.toStringTag]() { - return 'Redefined' + return "Redefined"; } go(arg) { return arg || this.message; } [Callable.CALL](arg) { - return this.go(arg) + return this.go(arg); } [CALL](arg) { - return this.go(arg) + return this.go(arg); } } -defaultTest(MyTestWithCall) +defaultTest(MyTestWithCall); describe(getTitle(MyTestWithCall), function () { - it('has same return as go method', function () { - assert(new MyTestWithCall('test').go('arg') === new MyTest('test')('arg')) + it("has same return as go method", function () { + assert(new MyTestWithCall("test").go("arg") === new MyTest("test")("arg")); }); it("correctly bounds this", function () { - const test = new MyTestWithCall('testing') - assert(test() === 'testing') - assert(test.go() === 'testing') - assert(test[CALL]() === 'testing') - assert(test[Callable.CALL]() === 'testing') - test.message = 'new message' - assert(test() === 'new message') - assert(test.go() === 'new message') - assert(test[CALL]() === 'new message') - assert(test[Callable.CALL]() === 'new message') - test.go = () => 'different' - assert(test.go() === 'different') - assert(test[CALL]() === 'different') - assert(test[Callable.CALL]() === 'different') - test[CALL] = () => 'called' - assert(test.go() === 'different') - assert(test[CALL]() === 'called') - assert(test[Callable.CALL]() === 'called') - assert(test.message === 'new message') - }); - it('has own string tag Redefined', function () { - assert(Object.prototype.toString.call(new MyTestWithCall('test')) === '[object Redefined]') - }) + const test = new MyTestWithCall("testing"); + assert(test() === "testing"); + assert(test.go() === "testing"); + assert(test[CALL]() === "testing"); + assert(test[Callable.CALL]() === "testing"); + test.message = "new message"; + assert(test() === "new message"); + assert(test.go() === "new message"); + assert(test[CALL]() === "new message"); + assert(test[Callable.CALL]() === "new message"); + test.go = () => "different"; + assert(test.go() === "different"); + assert(test[CALL]() === "different"); + assert(test[Callable.CALL]() === "different"); + test[CALL] = () => "called"; + assert(test.go() === "different"); + assert(test[CALL]() === "called"); + assert(test[Callable.CALL]() === "called"); + assert(test.message === "new message"); + }); + it("has own string tag Redefined", function () { + assert( + Object.prototype.toString.call(new MyTestWithCall("test")) === + "[object Redefined]" + ); + }); it("supports property redefine", function () { - const obj = new MyTestWithCall('testing') + const obj = new MyTestWithCall("testing"); assert(obj() === obj.go()); obj.go = () => { - return null - } - assert(obj() === null) - assert(obj.go() === null) + return null; + }; + assert(obj() === null); + assert(obj.go() === null); obj.go = function () { - return MyTestWithCall - } - assert(obj() === MyTestWithCall) - assert(obj.go() === MyTestWithCall) + return MyTestWithCall; + }; + assert(obj() === MyTestWithCall); + assert(obj.go() === MyTestWithCall); obj[CALL] = function () { - return 'check' - } - assert(obj() === 'check') - assert(obj.go() === MyTestWithCall) - }); - it('has only one [Callable.CALL] method', function () { - assert(MyTestWithCall[CALL] === MyTestWithCall[Callable.CALL] && Callable.CALL === CALL) - }) + return "check"; + }; + assert(obj() === "check"); + assert(obj.go() === MyTestWithCall); + }); + it("has only one [Callable.CALL] method", function () { + assert( + MyTestWithCall[CALL] === MyTestWithCall[Callable.CALL] && + Callable.CALL === CALL + ); + }); }); diff --git a/src/test/typeCheck.ts b/src/test/typeCheck.ts deleted file mode 100644 index c0274f1..0000000 --- a/src/test/typeCheck.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { expectType } from "ts-expect"; -import Callable, { CALL, OverrideCall } from "callable-instance"; - -class RepeaterWithFuncGeneric extends Callable<(x: string) => string, 'go'>{ - constructor(public count: number) { - super('go'); - } - - go(arg: string): string { - return arg.repeat(this.count); - } -} - -describe("Callable With Func Generic and custom property (TypeScript)", function () { - - it("is callable", function () { - expectType<(x: string) => string>(new RepeaterWithFuncGeneric(1)); - // @ts-expect-error wrong type for constructor - new RepeaterWithFuncGeneric("testing"); - // @ts-expect-error wrong type for method - new RepeaterWithFuncGeneric(5).go(5); - // Valid propert access. - new RepeaterWithFuncGeneric(5).count = 4; - }); - - it("is an object", function () { - expectType(new RepeaterWithFuncGeneric(5)); - expectType<(x: string) => string>(new RepeaterWithFuncGeneric(5).go); - }); - - it("is an instance of Repeater", function () { - expectType(new RepeaterWithFuncGeneric(5)); - //expectType(new Repeater(5)); - expectType(new RepeaterWithFuncGeneric(5)); - expectType(new RepeaterWithFuncGeneric(5)); - }); -}); - -interface IRepeaterWithCall { - [CALL](arg: string): string -} - -class RepeaterWithCALL extends Callable { - constructor(public count: number) { - super(); - } - - go(arg: string): string { - return arg.repeat(this.count); - } - - // [CALL] = '234' - [CALL](arg: string): string { - return this.go(arg) - } -} - -class RepeaterWithTest extends Callable<{ (arg: 'test'): string, (arg: number): number, ttt: 't' }> { - constructor(public count: number) { - super(); - } - - go(arg: string): string { - return arg.repeat(this.count); - } - - // [CALL] = '234' - [CALL](arg: string): string { - return this.go(arg) - } -} - -const test23 = new RepeaterWithTest(23) -const ggg = test23('test') -test23(23) -test23.ttt -const test = new RepeaterWithCALL(4) -test.go('23') -// const TTTTTT = test.CALL -const test2 = test('23') - -describe("Callable With CALL Generic (TypeScript)", function () { - it("is callable", function () { - expectType<(x: string) => string>(new RepeaterWithCALL(1)); - // @ts-expect-error wrong type for constructor - new RepeaterWithCALL("testing"); - // @ts-expect-error wrong type for method - new RepeaterWithCALL(5).go(5); - // Valid propert access. - new RepeaterWithCALL(5).count = 4; - }); - - it("is an object", function () { - expectType(new RepeaterWithCALL(5)); - expectType<(x: string) => string>(new RepeaterWithCALL(5).go); - }); - - it("is an instance of Repeater", function () { - expectType(new RepeaterWithCALL(5)); - //expectType(new Repeater(5)); - expectType(new RepeaterWithCALL(5)); - expectType(new RepeaterWithCALL(5)); - }); -}); - -interface IRepeaterWithCallableCall { - // go: (arg: string): string - [Callable.CALL](arg: string): string - // [Callable.CALL]: string - -} - -class RepeaterWithCallableCALL extends Callable { - constructor(public count: number) { - super(); - } - - go(arg: string): string { - return arg.repeat(this.count); - } - - [Callable.CALL](arg: string): string { - return this.go(arg) - } -} - -const check = new RepeaterWithCallableCALL(23) -check('23') - -describe("Callable With Callable.CALL Generic (TypeScript)", function () { - it("is callable", function () { - expectType<(x: string) => string>(new RepeaterWithCallableCALL(1)); - // @ts-expect-error wrong type for constructor - new RepeaterWithCallableCALL("testing"); - // @ts-expect-error wrong type for method - new RepeaterWithCallableCALL(5).go(5); - // Valid propert access. - new RepeaterWithCallableCALL(5).count = 4; - }); - - it("is an object", function () { - expectType(new RepeaterWithCallableCALL(5)); - expectType<(x: string) => string>(new RepeaterWithCallableCALL(5).go); - }); - - it("is an instance of Repeater", function () { - expectType(new RepeaterWithCallableCALL(5)); - //expectType(new Repeater(5)); - expectType(new RepeaterWithCallableCALL(5)); - expectType(new RepeaterWithCallableCALL(5)); - }); -}); - -interface IRepeaterWithCustomProperty { - $call(arg: string): string -} - -class RepeaterWithCustomProperty extends Callable { - constructor(public count: number) { - super("$call"); - } - - go(arg: string): string { - return arg.repeat(this.count); - } - - $call(arg: string): string { - return this.go(arg) - } -} - -describe("Callable With Custom Property Generic (TypeScript)", function () { - it("is callable", function () { - expectType<(x: string) => string>(new RepeaterWithCustomProperty(1)); - // @ts-expect-error wrong type for constructor - new RepeaterWithCustomProperty("testing"); - // @ts-expect-error wrong type for method - new RepeaterWithCustomProperty(5).go(5); - // Valid propert access. - new RepeaterWithCustomProperty(5).count = 4; - }); - - it("is an object", function () { - expectType(new RepeaterWithCustomProperty(5)); - expectType<(x: string) => string>(new RepeaterWithCustomProperty(5).go); - }); - - it("is an instance of Repeater", function () { - expectType(new RepeaterWithCustomProperty(5)); - //expectType(new Repeater(5)); - expectType(new RepeaterWithCustomProperty(5)); - expectType(new RepeaterWithCustomProperty(5)); - }); -}); - - -class TestCallable extends Callable{ - constructor() { - super('test') - } - static readonly t = 'ttt' - - test() { - return 'test' - } -} - -const testst = new TestCallable() - -class TestCallableExtends extends (TestCallable as OverrideCall){ - constructor() { - super() - } - go34(){ - return 23 - } - test() { - return '23' - } -} - -class TestCallableExtendsS extends (TestCallable as OverrideCall){ - constructor() { - super() - } - test() { - return 'test' - } -} - -const tttttttt = TestCallableExtends.t -const ttttttt2t = TestCallableExtendsS.t -// TestCallableExtendsS.t = 2 - -const newewe = new TestCallableExtends() -newewe.test() -const ttt = newewe() \ No newline at end of file From 56f3c40db3dd5b0abaa69c802701f0dbd55e329a Mon Sep 17 00:00:00 2001 From: pabadm Date: Wed, 25 Oct 2023 10:21:44 +0200 Subject: [PATCH 08/60] typo override (meant overload) fix --- lib/index.d.ts | 2 +- src/test/classGeneric.ts | 32 +++++++-------- src/test/funcGeneric.ts | 60 ++++++++++++++-------------- src/test/interfaceGeneric.ts | 76 ++++++++++++++++++------------------ 4 files changed, 85 insertions(+), 85 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index 7488f24..e9f94cb 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -18,7 +18,7 @@ declare module "callable-instance" { interface CloneFuncFromClass { /** - * For TS generics and function override support use interface or function type for Callable + * For TS generics and function overload support use interface or function type for Callable */ (...args: Parameters[P]>): ReturnType[P]>; } diff --git a/src/test/classGeneric.ts b/src/test/classGeneric.ts index 3dbd85d..7b7f6f5 100644 --- a/src/test/classGeneric.ts +++ b/src/test/classGeneric.ts @@ -54,7 +54,7 @@ describe("Callable With Class Generic and custom property (TypeScript)", functio //// override is not supported in class generic -// describe("Callable With TS Class Override Generic and custom property (TypeScript)", function () { +// describe("Callable With TS Class Overload Generic and custom property (TypeScript)", function () { // it("is callable", function () { // expectType<{ (x: string): string, (x: number): number }>(new RepeaterWithTSClassOverride(1)); // // @ts-expect-error wrong type for constructor @@ -83,7 +83,7 @@ describe("Callable With Class Generic and custom property (TypeScript)", functio -class RepeaterWithOverridenFunc extends (RepeaterWithClassGeneric as OverrideCall){ +class RepeaterWithClassOverride extends (RepeaterWithClassGeneric as OverrideCall){ constructor() { super(23) } @@ -92,31 +92,31 @@ class RepeaterWithOverridenFunc extends (RepeaterWithClassGeneric as OverrideCal } } -describe("Callable With Func Overriding Generic and custom property (TypeScript)", function () { +describe("Callable With Class Override Generic and custom property (TypeScript)", function () { it("is callable", function () { - expectType<() => number>(new RepeaterWithOverridenFunc()); + expectType<() => number>(new RepeaterWithClassOverride()); // @ts-expect-error wrong type for constructor - new RepeaterWithOverridenFunc()("testing"); + new RepeaterWithClassOverride()("testing"); // @ts-expect-error wrong type for method - new RepeaterWithOverridenFunc()(5).go(5); + new RepeaterWithClassOverride()(5).go(5); // Valid propert access. - new RepeaterWithOverridenFunc().count = 4; + new RepeaterWithClassOverride().count = 4; }); it("is an object", function () { - expectType(new RepeaterWithOverridenFunc()); - expectType<() => number>(new RepeaterWithOverridenFunc().go); - expectType<() => number>(new RepeaterWithOverridenFunc()) - expectType<(x: number) => number>(new RepeaterWithOverridenFunc().go) + expectType(new RepeaterWithClassOverride()); + expectType<() => number>(new RepeaterWithClassOverride().go); + expectType<() => number>(new RepeaterWithClassOverride()) + expectType<(x: number) => number>(new RepeaterWithClassOverride().go) }); it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class - // expectType(new RepeaterWithOverridenFunc()); - expectType(new RepeaterWithOverridenFunc()); - expectType>(new RepeaterWithOverridenFunc()); - expectType(new RepeaterWithOverridenFunc()); - expectType(new RepeaterWithOverridenFunc()); + // expectType(new RepeaterWithClassOverride()); + expectType(new RepeaterWithClassOverride()); + expectType>(new RepeaterWithClassOverride()); + expectType(new RepeaterWithClassOverride()); + expectType(new RepeaterWithClassOverride()); }); }); \ No newline at end of file diff --git a/src/test/funcGeneric.ts b/src/test/funcGeneric.ts index e2ca7c4..4f3d8a5 100644 --- a/src/test/funcGeneric.ts +++ b/src/test/funcGeneric.ts @@ -39,12 +39,12 @@ describe("Callable With Func Generic and custom property (TypeScript)", function }); }); -interface FuncOverride { +interface IFuncOverload { (x: number): number; (x: string): string; } -class RepeaterWithTSFuncOverride extends Callable { +class RepeaterWithFuncOverload extends Callable { constructor(public count: number) { super("go"); } @@ -54,44 +54,44 @@ class RepeaterWithTSFuncOverride extends Callable { } } -describe("Callable With Func Overriding Generic and custom property (TypeScript)", function () { +describe("Callable With Func overload Generic and custom property (TypeScript)", function () { it("is callable", function () { expectType<{ (x: string): string; (x: number): number }>( - new RepeaterWithTSFuncOverride(1) + new RepeaterWithFuncOverload(1) ); // @ts-expect-error wrong type for constructor - new RepeaterWithTSFuncOverride("testing"); + new RepeaterWithFuncOverload("testing"); // @ts-expect-error wrong type for method - new RepeaterWithTSFuncOverride(5).go(5); + new RepeaterWithFuncOverload(5).go(5); // Valid propert access. - new RepeaterWithTSFuncOverride(5).count = 4; + new RepeaterWithFuncOverload(5).count = 4; }); it("is an object", function () { - expectType(new RepeaterWithTSFuncOverride(5)); - expectType<(x: string) => string>(new RepeaterWithTSFuncOverride(5).go); + expectType(new RepeaterWithFuncOverload(5)); + expectType<(x: string) => string>(new RepeaterWithFuncOverload(5).go); expectType<{ (x: string): string; (x: number): number }>( - new RepeaterWithTSFuncOverride(5) + new RepeaterWithFuncOverload(5) ); expectType<(x: string) => string>(() => - new RepeaterWithTSFuncOverride(5)("23") + new RepeaterWithFuncOverload(5)("23") ); expectType<(x: number) => number>(() => - new RepeaterWithTSFuncOverride(5)(23) + new RepeaterWithFuncOverload(5)(23) ); }); it("is an instance of Repeater", function () { - expectType(new RepeaterWithTSFuncOverride(5)); + expectType(new RepeaterWithFuncOverload(5)); expectType>( - new RepeaterWithTSFuncOverride(5) + new RepeaterWithFuncOverload(5) ); - expectType(new RepeaterWithTSFuncOverride(5)); - expectType(new RepeaterWithTSFuncOverride(5)); + expectType(new RepeaterWithFuncOverload(5)); + expectType(new RepeaterWithFuncOverload(5)); }); }); -class RepeaterWithOverridenFunc extends (RepeaterWithFuncGeneric as OverrideCall< +class RepeaterWithFuncOverride extends (RepeaterWithFuncGeneric as OverrideCall< typeof RepeaterWithFuncGeneric >)<() => number, "go"> { constructor() { @@ -104,30 +104,30 @@ class RepeaterWithOverridenFunc extends (RepeaterWithFuncGeneric as OverrideCall describe("Callable With TS Func Override Generic and custom property (TypeScript)", function () { it("is callable", function () { - expectType<() => number>(new RepeaterWithOverridenFunc()); + expectType<() => number>(new RepeaterWithFuncOverride()); // @ts-expect-error wrong type for constructor - new RepeaterWithOverridenFunc()("testing"); + new RepeaterWithFuncOverride()("testing"); // @ts-expect-error wrong type for method - new RepeaterWithOverridenFunc()(5).go(5); + new RepeaterWithFuncOverride()(5).go(5); // Valid propert access. - new RepeaterWithOverridenFunc().count = 4; + new RepeaterWithFuncOverride().count = 4; }); it("is an object", function () { - expectType(new RepeaterWithOverridenFunc()); - expectType<() => number>(new RepeaterWithOverridenFunc().go); - expectType<() => number>(new RepeaterWithOverridenFunc()); - expectType<(x: number) => number>(new RepeaterWithOverridenFunc().go); + expectType(new RepeaterWithFuncOverride()); + expectType<() => number>(new RepeaterWithFuncOverride().go); + expectType<() => number>(new RepeaterWithFuncOverride()); + expectType<(x: number) => number>(new RepeaterWithFuncOverride().go); }); it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class - // expectType(new RepeaterWithOverridenFunc()); - expectType(new RepeaterWithOverridenFunc()); + // expectType(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithFuncOverride()); expectType>( - new RepeaterWithOverridenFunc() + new RepeaterWithFuncOverride() ); - expectType(new RepeaterWithOverridenFunc()); - expectType(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithFuncOverride()); + expectType(new RepeaterWithFuncOverride()); }); }); diff --git a/src/test/interfaceGeneric.ts b/src/test/interfaceGeneric.ts index 471ced9..475351b 100644 --- a/src/test/interfaceGeneric.ts +++ b/src/test/interfaceGeneric.ts @@ -50,14 +50,14 @@ describe("Callable With Interface Generic and custom property (TypeScript)", fun }); }); -interface FuncOverride { +interface IInterfaceOverload { go(x: number): number; go(x: string): string; } -class RepeaterWithTSInterfaceOverride - extends Callable - implements FuncOverride +class RepeaterWithInterfaceOverload + extends Callable + implements IInterfaceOverload { constructor(public count: number) { super("go"); @@ -70,57 +70,57 @@ class RepeaterWithTSInterfaceOverride } } -describe("Callable With TS Interface Override Generic and custom property (TypeScript)", function () { +describe("Callable With TS Interface Overload Generic and custom property (TypeScript)", function () { it("is callable", function () { expectType<{ (x: string): string; (x: number): number }>( - new RepeaterWithTSInterfaceOverride(1) + new RepeaterWithInterfaceOverload(1) ); // @ts-expect-error wrong type for constructor - new RepeaterWithTSInterfaceOverride("testing"); - new RepeaterWithTSInterfaceOverride(5).go(5); + new RepeaterWithInterfaceOverload("testing"); + new RepeaterWithInterfaceOverload(5).go(5); // Valid propert access. - new RepeaterWithTSInterfaceOverride(5).count = 4; + new RepeaterWithInterfaceOverload(5).count = 4; }); it("is an object", function () { - expectType( - new RepeaterWithTSInterfaceOverride(5) + expectType( + new RepeaterWithInterfaceOverload(5) ); expectType<(x: string) => string>( - new RepeaterWithTSInterfaceOverride(5).go + new RepeaterWithInterfaceOverload(5).go ); expectType<{ (x: string): string; (x: number): number }>( - new RepeaterWithTSInterfaceOverride(5) + new RepeaterWithInterfaceOverload(5) ); expectType<(x: string) => string>(() => - new RepeaterWithTSInterfaceOverride(5)("23") + new RepeaterWithInterfaceOverload(5)("23") ); expectType<(x: number) => number>(() => - new RepeaterWithTSInterfaceOverride(5)(23) + new RepeaterWithInterfaceOverload(5)(23) ); }); it("is an instance of Repeater", function () { - expectType( - new RepeaterWithTSInterfaceOverride(5) + expectType( + new RepeaterWithInterfaceOverload(5) ); expectType>( - new RepeaterWithTSInterfaceOverride(5) + new RepeaterWithInterfaceOverload(5) ); - expectType(new RepeaterWithTSInterfaceOverride(5)); - expectType(new RepeaterWithTSInterfaceOverride(5)); + expectType(new RepeaterWithInterfaceOverload(5)); + expectType(new RepeaterWithInterfaceOverload(5)); }); }); -interface OverridenType { +interface IInterface { go(): number; } -class RepeaterWithOverridenFunc +class RepeaterWithInterfaceOverride extends (RepeaterWithInterfaceGeneric as OverrideCall< typeof RepeaterWithInterfaceGeneric - >) - implements OverridenType + >) + implements IInterface { constructor() { super(23); @@ -130,32 +130,32 @@ class RepeaterWithOverridenFunc } } -describe("Callable With Func Overriding Generic and custom property (TypeScript)", function () { +describe("Callable With Interface override Generic and custom property (TypeScript)", function () { it("is callable", function () { - expectType<() => number>(new RepeaterWithOverridenFunc()); + expectType<() => number>(new RepeaterWithInterfaceOverride()); // @ts-expect-error wrong type for constructor - new RepeaterWithOverridenFunc()("testing"); + new RepeaterWithInterfaceOverride()("testing"); // @ts-expect-error wrong type for method - new RepeaterWithOverridenFunc()(5).go(5); + new RepeaterWithInterfaceOverride()(5).go(5); // Valid propert access. - new RepeaterWithOverridenFunc().count = 4; + new RepeaterWithInterfaceOverride().count = 4; }); it("is an object", function () { - expectType(new RepeaterWithOverridenFunc()); - expectType<() => number>(new RepeaterWithOverridenFunc().go); - expectType<() => number>(new RepeaterWithOverridenFunc()); - expectType<(x: number) => number>(new RepeaterWithOverridenFunc().go); + expectType(new RepeaterWithInterfaceOverride()); + expectType<() => number>(new RepeaterWithInterfaceOverride().go); + expectType<() => number>(new RepeaterWithInterfaceOverride()); + expectType<(x: number) => number>(new RepeaterWithInterfaceOverride().go); }); it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class - // expectType(new RepeaterWithOverridenFunc()); - expectType(new RepeaterWithOverridenFunc()); + // expectType(new RepeaterWithInterfaceOverride()); + expectType(new RepeaterWithInterfaceOverride()); expectType>( - new RepeaterWithOverridenFunc() + new RepeaterWithInterfaceOverride() ); - expectType(new RepeaterWithOverridenFunc()); - expectType(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithInterfaceOverride()); + expectType(new RepeaterWithInterfaceOverride()); }); }); From 8200e94ccb686aecba985f7690bf1a8ef3107487 Mon Sep 17 00:00:00 2001 From: pabadm Date: Wed, 25 Oct 2023 10:28:43 +0200 Subject: [PATCH 09/60] useless test fix --- src/test/test.cjs | 11 ----------- src/test/test.mjs | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/src/test/test.cjs b/src/test/test.cjs index 5085acf..66ccee1 100644 --- a/src/test/test.cjs +++ b/src/test/test.cjs @@ -22,17 +22,6 @@ function defaultTest(Class, prototypes = []) { return describe(getTitle(Class, true), function () { it("is callable", function () { assert(typeof new Class("msg") === "function"); - assert( - (function () { - let test; - try { - test = new Class("msg")(); - } catch (e) { - return false; - } - return true; - })() - ); }); it("correctly inherits prototypes", function () { assert(typeof new Class("msg") === "function"); diff --git a/src/test/test.mjs b/src/test/test.mjs index 30abf5f..ba26bb9 100644 --- a/src/test/test.mjs +++ b/src/test/test.mjs @@ -20,17 +20,6 @@ function defaultTest(Class, prototypes = []) { return describe(getTitle(Class, true), function () { it("is callable", function () { assert(typeof new Class("msg") === "function"); - assert( - (function () { - let test; - try { - test = new Class("msg")(); - } catch (e) { - return false; - } - return true; - })() - ); }); it("correctly inherits prototypes", function () { assert(typeof new Class("msg") === "function"); From 49fd9b14eaab01f5e4a661a3a01f395a0a4ff121 Mon Sep 17 00:00:00 2001 From: pabadm Date: Wed, 25 Oct 2023 10:50:57 +0200 Subject: [PATCH 10/60] removed CALL export --- fixup.sh | 10 ----- lib/index.js | 30 ++++++++++++++ package.json | 14 +++---- src/index.js | 32 --------------- {src/test => test}/classGeneric.ts | 0 {src/test => test}/funcGeneric.ts | 0 {src/test => test}/interfaceGeneric.ts | 0 {src/test => test}/overrideCallProperties.ts | 0 {src/test => test}/test.cjs | 42 ++++++-------------- {src/test => test}/test.mjs | 26 ++++-------- tsconfig-cjs.json | 8 ---- tsconfig.json | 35 ---------------- 12 files changed, 54 insertions(+), 143 deletions(-) delete mode 100755 fixup.sh create mode 100644 lib/index.js delete mode 100644 src/index.js rename {src/test => test}/classGeneric.ts (100%) rename {src/test => test}/funcGeneric.ts (100%) rename {src/test => test}/interfaceGeneric.ts (100%) rename {src/test => test}/overrideCallProperties.ts (100%) rename {src/test => test}/test.cjs (83%) rename {src/test => test}/test.mjs (89%) delete mode 100644 tsconfig-cjs.json delete mode 100644 tsconfig.json diff --git a/fixup.sh b/fixup.sh deleted file mode 100755 index c0556e6..0000000 --- a/fixup.sh +++ /dev/null @@ -1,10 +0,0 @@ -cat >dist/cjs/package.json <dist/mjs/package.json < "different"; assert(test.go() === "different"); - assert(test[CALL]() === "different"); assert(test[Callable.CALL]() === "different"); - test[CALL] = () => "called"; + assert(test[Callable.CALL]() === "different"); + test[Callable.CALL] = () => "called"; assert(test.go() === "different"); - assert(test[CALL]() === "called"); + assert(test[Callable.CALL]() === "called"); assert(test[Callable.CALL]() === "called"); assert(test.message === "new message"); }); @@ -198,16 +186,10 @@ describe(getTitle(MyTestWithCall), function () { }; assert(obj() === MyTestWithCall); assert(obj.go() === MyTestWithCall); - obj[CALL] = function () { + obj[Callable.CALL] = function () { return "check"; }; assert(obj() === "check"); assert(obj.go() === MyTestWithCall); }); - it("has only one [Callable.CALL] method", function () { - assert( - MyTestWithCall[CALL] === MyTestWithCall[Callable.CALL] && - Callable.CALL === CALL - ); - }); }); diff --git a/src/test/test.mjs b/test/test.mjs similarity index 89% rename from src/test/test.mjs rename to test/test.mjs index ba26bb9..1e711f4 100644 --- a/src/test/test.mjs +++ b/test/test.mjs @@ -1,5 +1,5 @@ import assert from "assert"; -import Callable, { CALL } from "callable-instance"; +import Callable from "callable-instance"; function getTitle(Class, isDefault) { return `${Class.name}${isDefault ? " default" : ""} (mjs)`; @@ -11,9 +11,6 @@ describe(getTitle(Callable) + " Callable Class Test", function () { assert(new Callable() instanceof Function); assert(new Callable() instanceof Callable); }); - it("Callable.CALL symbol is equal to CALL symbol", function () { - assert(Callable.CALL === CALL); - }); }); function defaultTest(Class, prototypes = []) { @@ -145,9 +142,6 @@ class MyTestWithCall extends Callable { [Callable.CALL](arg) { return this.go(arg); } - [CALL](arg) { - return this.go(arg); - } } defaultTest(MyTestWithCall); @@ -160,20 +154,20 @@ describe(getTitle(MyTestWithCall), function () { const test = new MyTestWithCall("testing"); assert(test() === "testing"); assert(test.go() === "testing"); - assert(test[CALL]() === "testing"); + assert(test[Callable.CALL]() === "testing"); assert(test[Callable.CALL]() === "testing"); test.message = "new message"; assert(test() === "new message"); assert(test.go() === "new message"); - assert(test[CALL]() === "new message"); + assert(test[Callable.CALL]() === "new message"); assert(test[Callable.CALL]() === "new message"); test.go = () => "different"; assert(test.go() === "different"); - assert(test[CALL]() === "different"); assert(test[Callable.CALL]() === "different"); - test[CALL] = () => "called"; + assert(test[Callable.CALL]() === "different"); + test[Callable.CALL] = () => "called"; assert(test.go() === "different"); - assert(test[CALL]() === "called"); + assert(test[Callable.CALL]() === "called"); assert(test[Callable.CALL]() === "called"); assert(test.message === "new message"); }); @@ -196,16 +190,10 @@ describe(getTitle(MyTestWithCall), function () { }; assert(obj() === MyTestWithCall); assert(obj.go() === MyTestWithCall); - obj[CALL] = function () { + obj[Callable.CALL] = function () { return "check"; }; assert(obj() === "check"); assert(obj.go() === MyTestWithCall); }); - it("has only one [Callable.CALL] method", function () { - assert( - MyTestWithCall[CALL] === MyTestWithCall[Callable.CALL] && - Callable.CALL === CALL - ); - }); }); diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json deleted file mode 100644 index 576b1c0..0000000 --- a/tsconfig-cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "CommonJS", - "outDir": "dist/cjs", - "target": "es6", - }, -} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index a3a4a23..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "compilerOptions": { - /// for cjs - tsconfig-cjs.json /// - "module": "es6", - "outDir": "dist/mjs", - "target": "es6", - /// for cjs - tsconfig-cjs.json /// - "removeComments": true, - "allowJs": true, - "allowSyntheticDefaultImports": true, - "baseUrl": "src", - "declaration": false, - "esModuleInterop": true, - "inlineSourceMap": false, - "listEmittedFiles": false, - "listFiles": false, - "moduleResolution": "node", - "pretty": true, - "rootDir": "src", - "skipLibCheck": true, - "strict": true, - "traceResolution": false, - "typeRoots": [ - "node_modules/@types", "lib" - ] - }, - "include": [ "src" ], - "exclude": [ - "test", - "src/types", - "src/test", - "lib", - "node_modules", - ] -} \ No newline at end of file From f735d415bf6d5df8f4eea3ac319840eb138ad765 Mon Sep 17 00:00:00 2001 From: pabadm Date: Wed, 25 Oct 2023 10:53:04 +0200 Subject: [PATCH 11/60] removed CALL export from d.ts --- lib/index.d.ts | 2 +- test/classGeneric.ts | 2 +- test/overrideCallProperties.ts | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index e9f94cb..92ff1da 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,5 +1,5 @@ declare module "callable-instance" { - export const CALL: unique symbol; + const CALL: unique symbol; export type SCALL = typeof CALL; type BaseProperty = symbol | string | number; diff --git a/test/classGeneric.ts b/test/classGeneric.ts index 7b7f6f5..5f4c0f8 100644 --- a/test/classGeneric.ts +++ b/test/classGeneric.ts @@ -1,5 +1,5 @@ import { expectType } from "ts-expect"; -import Callable, { CALL, CallableConstructor, OverrideCall } from "callable-instance"; +import Callable, { CallableConstructor, OverrideCall } from "callable-instance"; // TESTS FOR CLASS-TYPE GENERICS diff --git a/test/overrideCallProperties.ts b/test/overrideCallProperties.ts index d464b00..b8f9d99 100644 --- a/test/overrideCallProperties.ts +++ b/test/overrideCallProperties.ts @@ -1,7 +1,5 @@ import { expectType } from "ts-expect"; import Callable, { - CALL, - CallableConstructor, OverrideCall, } from "callable-instance"; @@ -14,7 +12,7 @@ class MyClass extends Callable { public publicProperty = "public"; public readonly readonlyProperty = "readonly" as const; - private [CALL]() { + private [Callable.CALL]() { return 32; } } @@ -40,7 +38,7 @@ class Extended extends (MyClass as OverrideCall)< public newPublicProperty = "public"; public readonly newReadonlyProperty = "readonly" as const; - [CALL]() { + [Callable.CALL]() { return "str"; } } From e5d90dc128b10e4e3947a4a4d58c29e8dd6f0f30 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Wed, 25 Oct 2023 07:42:05 +0200 Subject: [PATCH 12/60] Update README.md --- README.md | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d7b0d09..8d88797 100644 --- a/README.md +++ b/README.md @@ -15,31 +15,39 @@ npm install callable-instance In the following example, we will create an `ExampleClass` class. The instances have all of the normal properties and methods, but are actually functions as well. ```javascript -import CallableInstance from "callable-instance"; +import Callable from "callable-instance"; // If you aren't using ES modules, you can use require: // var CallableInstance = require("callable-instance"); -class ExampleClass extends CallableInstance { +class ExampleClass extends Callable { constructor() { - // CallableInstance accepts the name of the property to use as the callable - // method. - super("instanceMethod"); + super(); } - - instanceMethod() { - console.log("instanceMethod called!"); + [Callable.CALL](arg) { + return arg } } var test = new ExampleClass(); // Invoke the method normally -test.instanceMethod(); +test[Callable.CALL](); // Call the instance itself, redirects to instanceMethod test(); // The instance is actually a closure bound to itself and can be used like a // normal function. test.apply(null, [1, 2, 3]); ``` +Usage of custom method name is also supported +```javascript +class ExampleClassWithCustomMethodName extends Callable { + constructor(){ + super('myMethod') + } + myMethod(arg){ + return arg + } +} +``` TypeScript is also supported. `CallableInstance` is generic, accepting a tuple of arguments and a return type. From 78565022456396dc07dd95aaa49c435e6879175d Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Wed, 25 Oct 2023 11:07:34 +0200 Subject: [PATCH 13/60] Update README.md --- README.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 8d88797..2ff0b3a 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,8 @@ test(); // normal function. test.apply(null, [1, 2, 3]); ``` -Usage of custom method name is also supported +> **_NOTE:_** Usage of custom method name is also supported. + ```javascript class ExampleClassWithCustomMethodName extends Callable { constructor(){ @@ -49,24 +50,88 @@ class ExampleClassWithCustomMethodName extends Callable { } ``` -TypeScript is also supported. `CallableInstance` is generic, accepting a tuple of arguments and a return type. +### Typescript + + +`Callable` has full typescript support. + +1. **Using interface** + +```typescript +// Callable has 2 generics +// 1st is for class | interface | function (for extracting type of call signature) +// 2nd is for propertyName (string | symbol | number). defaults to Callable.CALL + +interface IExampleClass { + [Callable.CALL](arg: A): A +} + +class ExampleClass extends Callable implements IExampleClass { + constructor(){ + super() + } + [Callable.CALL](arg: A){ + return arg + } +} +``` + +2. **Using function type** ```typescript -import CallableInstance from "callable-instance"; +class ExampleClass extends Callable<(arg: A) => A> { + constructor(){ + super() + } + [Callable.CALL](arg: A){ + return arg + } +} +``` -class ExampleClass extends CallableInstance<[number], string> { +3. **Using class type** +```typescript +class ExampleClass extends Callable { + constructor(){ + super() + } + [Callable.CALL](arg: string){ + return arg + } +} +``` +> **_NOTE:_** For function overload or generics use Interface or Function variant. + +#### Override Call + +Due to typescript limitations module also provides OverrideCall type. +It can be used to override call signature in child classes. + +```typescript +// Override call has 3 generics but must be written only in one way +// class Child extends (Parent as OverrideCall) +// Child can be interface | class | function (for extracting type of new call signature) +// propertyName can be string | symbol | number. defaults to Callable.CALL + +class ExampleClass extends Callable<() => string> { constructor() { - super("instanceMethod"); + super(); } - - instanceMethod(input: number): string { - return `${input}`; + [Callable.CALL]() { + return "test"; } } -``` -Note that the types specified may differ from the argument and return value types of the target method; this is an error due to a limitation of TypeScript. +class ExampleClassChild extends (ExampleClass as OverrideCall)<() => number> { + constructor(){ + super(); + } + [Callable.CALL](){ + return 100 + } +} +``` ### Inherited Properties @@ -81,7 +146,7 @@ var test = new ExampleClass(); test.name = "hello!"; console.log(test.name); // Will print 'instanceMethod' -class NameableClass extends CallableInstance { +class NameableClass extends Callable { constructor() { super("instanceMethod"); Object.defineProperty(this, "name", { From f6d3ccbaaff0c33f83127ef86033f8fa9b060a5e Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Wed, 25 Oct 2023 11:11:34 +0200 Subject: [PATCH 14/60] Update README.md --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2ff0b3a..5a610ea 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ class ExampleClassWithCustomMethodName extends Callable { ``` -### Typescript +## Typescript `Callable` has full typescript support. @@ -61,7 +61,7 @@ class ExampleClassWithCustomMethodName extends Callable { ```typescript // Callable has 2 generics // 1st is for class | interface | function (for extracting type of call signature) -// 2nd is for propertyName (string | symbol | number). defaults to Callable.CALL +// 2nd optional generic is for propertyName (string | symbol | number). defaults to Callable.CALL interface IExampleClass { [Callable.CALL](arg: A): A @@ -102,7 +102,7 @@ class ExampleClass extends Callable { ``` > **_NOTE:_** For function overload or generics use Interface or Function variant. -#### Override Call +### **Override Call** Due to typescript limitations module also provides OverrideCall type. It can be used to override call signature in child classes. @@ -110,8 +110,9 @@ It can be used to override call signature in child classes. ```typescript // Override call has 3 generics but must be written only in one way // class Child extends (Parent as OverrideCall) -// Child can be interface | class | function (for extracting type of new call signature) -// propertyName can be string | symbol | number. defaults to Callable.CALL +// 1st generic is always Parent +// 2nd generic is Child. Can be interface | class | function +// 3rd optional generic is propertyName can be string | symbol | number. defaults to Callable.CALL class ExampleClass extends Callable<() => string> { constructor() { From bc246caa32647c189e0ed5c687a59c05fc7a2d46 Mon Sep 17 00:00:00 2001 From: pabadm Date: Wed, 25 Oct 2023 14:44:36 +0200 Subject: [PATCH 15/60] added test for function with generic --- lib/index.d.ts | 1 - test/classGeneric.ts | 140 ++++++++++++++++++++------------------- test/funcGeneric.ts | 50 +++++++++++++- test/interfaceGeneric.ts | 62 +++++++++++++++-- 4 files changed, 178 insertions(+), 75 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index 92ff1da..9e7445f 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -50,7 +50,6 @@ declare module "callable-instance" { >; } - // pick for omitting call signature type PickProperties> = { [k in keyof Obj]: Obj[k]; }; diff --git a/test/classGeneric.ts b/test/classGeneric.ts index 5f4c0f8..91af8dc 100644 --- a/test/classGeneric.ts +++ b/test/classGeneric.ts @@ -3,43 +3,46 @@ import Callable, { CallableConstructor, OverrideCall } from "callable-instance"; // TESTS FOR CLASS-TYPE GENERICS -class RepeaterWithClassGeneric extends Callable{ - constructor(public count: number) { - super('go'); - } - - go(arg: string): string { - return arg.repeat(this.count); - } +class RepeaterWithClassGeneric extends Callable< + typeof RepeaterWithClassGeneric, + "go" +> { + constructor(public count: number) { + super("go"); + } + + go(arg: string): string { + return arg.repeat(this.count); + } } describe("Callable With Class Generic and custom property (TypeScript)", function () { - - it("is callable", function () { - expectType<(x: string) => string>(new RepeaterWithClassGeneric(1)); - // @ts-expect-error wrong type for constructor - new RepeaterWithClassGeneric("testing"); - // @ts-expect-error wrong type for method - new RepeaterWithClassGeneric(5).go(5); - // Valid propert access. - new RepeaterWithClassGeneric(5).count = 4; - }); - - it("is an object", function () { - expectType(new RepeaterWithClassGeneric(5)); - expectType<(x: string) => string>(new RepeaterWithClassGeneric(5)); - expectType<(x: string) => string>(new RepeaterWithClassGeneric(5).go); - }); - - it("is an instance of Repeater", function () { - expectType(new RepeaterWithClassGeneric(5)); - expectType>(new RepeaterWithClassGeneric(5)); - expectType(new RepeaterWithClassGeneric(5)); - expectType(new RepeaterWithClassGeneric(5)); - }); + it("is callable", function () { + expectType<(x: string) => string>(new RepeaterWithClassGeneric(1)); + // @ts-expect-error wrong type for constructor + new RepeaterWithClassGeneric("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithClassGeneric(5).go(5); + // Valid propert access. + new RepeaterWithClassGeneric(5).count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithClassGeneric(5)); + expectType<(x: string) => string>(new RepeaterWithClassGeneric(5)); + expectType<(x: string) => string>(new RepeaterWithClassGeneric(5).go); + }); + + it("is an instance of Repeater", function () { + expectType(new RepeaterWithClassGeneric(5)); + expectType>( + new RepeaterWithClassGeneric(5) + ); + expectType(new RepeaterWithClassGeneric(5)); + expectType(new RepeaterWithClassGeneric(5)); + }); }); - // class RepeaterWithTSClassOverride extends Callable { // constructor(public count: number) { // super('go'); @@ -81,42 +84,43 @@ describe("Callable With Class Generic and custom property (TypeScript)", functio // }); // }); - - -class RepeaterWithClassOverride extends (RepeaterWithClassGeneric as OverrideCall){ - constructor() { - super(23) - } - go() { - return 23 - } +class RepeaterWithClassOverride extends (RepeaterWithClassGeneric as OverrideCall< + typeof RepeaterWithClassGeneric +>) { + constructor() { + super(23); + } + go() { + return 23; + } } describe("Callable With Class Override Generic and custom property (TypeScript)", function () { - it("is callable", function () { - expectType<() => number>(new RepeaterWithClassOverride()); - // @ts-expect-error wrong type for constructor - new RepeaterWithClassOverride()("testing"); - // @ts-expect-error wrong type for method - new RepeaterWithClassOverride()(5).go(5); - // Valid propert access. - new RepeaterWithClassOverride().count = 4; - }); - - it("is an object", function () { - expectType(new RepeaterWithClassOverride()); - expectType<() => number>(new RepeaterWithClassOverride().go); - expectType<() => number>(new RepeaterWithClassOverride()) - expectType<(x: number) => number>(new RepeaterWithClassOverride().go) - - }); - - it("is an instance of Repeater", function () { - // is not passed because for typescript OverrideCall is other class - // expectType(new RepeaterWithClassOverride()); - expectType(new RepeaterWithClassOverride()); - expectType>(new RepeaterWithClassOverride()); - expectType(new RepeaterWithClassOverride()); - expectType(new RepeaterWithClassOverride()); - }); -}); \ No newline at end of file + it("is callable", function () { + expectType<() => number>(new RepeaterWithClassOverride()); + // @ts-expect-error wrong type for constructor + new RepeaterWithClassOverride()("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithClassOverride()(5).go(5); + // Valid propert access. + new RepeaterWithClassOverride().count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithClassOverride()); + expectType<() => number>(new RepeaterWithClassOverride().go); + expectType<() => number>(new RepeaterWithClassOverride()); + expectType<(x: number) => number>(new RepeaterWithClassOverride().go); + }); + + it("is an instance of Repeater", function () { + // is not passed because for typescript OverrideCall is other class + // expectType(new RepeaterWithClassOverride()); + expectType(new RepeaterWithClassOverride()); + expectType>( + new RepeaterWithClassOverride() + ); + expectType(new RepeaterWithClassOverride()); + expectType(new RepeaterWithClassOverride()); + }); +}); diff --git a/test/funcGeneric.ts b/test/funcGeneric.ts index 4f3d8a5..547998f 100644 --- a/test/funcGeneric.ts +++ b/test/funcGeneric.ts @@ -2,7 +2,7 @@ import { expectType } from "ts-expect"; import Callable, { CallableConstructor, OverrideCall } from "callable-instance"; // TESTS FOR FUNCTION-TYPE GENERICS -class RepeaterWithFuncGeneric extends Callable<(x: string)=> string, "go"> { +class RepeaterWithFuncGeneric extends Callable<(x: string) => string, "go"> { constructor(public count: number) { super("go"); } @@ -131,3 +131,51 @@ describe("Callable With TS Func Override Generic and custom property (TypeScript expectType(new RepeaterWithFuncOverride()); }); }); + +type GenericFunc = (arg: G) => G; + +class RepeaterWithGenericFunc extends Callable { + constructor(public count: number) { + super(); + } + [Callable.CALL](arg: G) { + return arg; + } +} + +describe("Callable With Generic Function Generic and custom property (TypeScript)", function () { + it("is callable", function () { + expectType<(arg: G, B: b) => G>( + new RepeaterWithGenericFunc(23) + ); + new RepeaterWithGenericFunc(2)("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithGenericFunc()(5).go(5); + // Valid propert access. + new RepeaterWithGenericFunc(2).count = 4; + }); + + it("is an object", function () { + expectType(new RepeaterWithGenericFunc(23)); + expectType<(arg: G) => G>( + new RepeaterWithGenericFunc(23)[Callable.CALL] + ); + expectType<(arg: G) => G>( + new RepeaterWithGenericFunc(23) + ); + expectType<(x: number) => number>( + new RepeaterWithGenericFunc(23)[Callable.CALL] + ); + }); + + it("is an instance of Repeater", function () { + // is not passed because for typescript OverrideCall is other class + // expectType(new RepeaterWithOverridenFunc()); + expectType(new RepeaterWithGenericFunc(23)); + expectType>( + new RepeaterWithGenericFunc(23) + ); + expectType(new RepeaterWithGenericFunc(23)); + expectType(new RepeaterWithGenericFunc(23)); + }); +}); \ No newline at end of file diff --git a/test/interfaceGeneric.ts b/test/interfaceGeneric.ts index 475351b..23ecabd 100644 --- a/test/interfaceGeneric.ts +++ b/test/interfaceGeneric.ts @@ -86,9 +86,7 @@ describe("Callable With TS Interface Overload Generic and custom property (TypeS expectType( new RepeaterWithInterfaceOverload(5) ); - expectType<(x: string) => string>( - new RepeaterWithInterfaceOverload(5).go - ); + expectType<(x: string) => string>(new RepeaterWithInterfaceOverload(5).go); expectType<{ (x: string): string; (x: number): number }>( new RepeaterWithInterfaceOverload(5) ); @@ -142,7 +140,9 @@ describe("Callable With Interface override Generic and custom property (TypeScri }); it("is an object", function () { - expectType(new RepeaterWithInterfaceOverride()); + expectType( + new RepeaterWithInterfaceOverride() + ); expectType<() => number>(new RepeaterWithInterfaceOverride().go); expectType<() => number>(new RepeaterWithInterfaceOverride()); expectType<(x: number) => number>(new RepeaterWithInterfaceOverride().go); @@ -151,7 +151,9 @@ describe("Callable With Interface override Generic and custom property (TypeScri it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class // expectType(new RepeaterWithInterfaceOverride()); - expectType(new RepeaterWithInterfaceOverride()); + expectType( + new RepeaterWithInterfaceOverride() + ); expectType>( new RepeaterWithInterfaceOverride() ); @@ -159,3 +161,53 @@ describe("Callable With Interface override Generic and custom property (TypeScri expectType(new RepeaterWithInterfaceOverride()); }); }); + +interface IGenericInterface { + go(g: G): G; +} + +class RepeaterWithGenericInterface + extends Callable + implements IGenericInterface +{ + constructor() { + super('go'); + } + public count = 23 + go(g) { + return g + } +} + +describe("Callable With Generic Interface Generic and custom property (TypeScript)", function () { + it("is callable", function () { + expectType<(arg: G) => G>(new RepeaterWithGenericInterface()); + new RepeaterWithGenericInterface()("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithGenericInterface()(5).go(5); + // Valid propert access. + new RepeaterWithGenericInterface().count = 4; + }); + + it("is an object", function () { + expectType( + new RepeaterWithGenericInterface() + ); + expectType<(arg: G) => G>(new RepeaterWithGenericInterface().go); + expectType<(arg: G) => G>(new RepeaterWithGenericInterface()); + expectType<(arg: G) => G>(new RepeaterWithGenericInterface().go); + }); + + it("is an instance of Repeater", function () { + // is not passed because for typescript OverrideCall is other class + // expectType(new RepeaterWithInterfaceOverride()); + expectType( + new RepeaterWithGenericInterface() + ); + expectType>( + new RepeaterWithGenericInterface() + ); + expectType(new RepeaterWithGenericInterface()); + expectType(new RepeaterWithGenericInterface()); + }); +}); From d9c20eb4815178cc447eaccf7b8d9bc5d346c3b0 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Wed, 25 Oct 2023 11:20:14 +0200 Subject: [PATCH 16/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a610ea..53f5a17 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ class ExampleClassChild extends (ExampleClass as OverrideCall Date: Wed, 25 Oct 2023 11:26:45 +0200 Subject: [PATCH 17/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 53f5a17..d14ba15 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ It can be used to override call signature in child classes. ```typescript // Override call has 3 generics but must be written only in one way // class Child extends (Parent as OverrideCall) -// 1st generic is always Parent +// 1st generic is Parent // 2nd generic is Child. Can be interface | class | function // 3rd optional generic is propertyName can be string | symbol | number. defaults to Callable.CALL From 110f520004d550e53ca502916386311d4bfdf47b Mon Sep 17 00:00:00 2001 From: pabadm Date: Fri, 27 Oct 2023 03:08:05 +0200 Subject: [PATCH 18/60] added new way to create callable object --- lib/index.d.ts | 13 +- lib/index.js | 56 +++- test/clone.cjs | 239 ++++++++++++++++++ test/clone.mjs | 239 ++++++++++++++++++ test/makeCallable.cjs | 212 ++++++++++++++++ test/makeCallable.mjs | 212 ++++++++++++++++ test/{test.cjs => newCallable.cjs} | 20 +- test/{test.mjs => newCallable.mjs} | 12 + ...rrideCallProperties.ts => overrideCall.ts} | 0 9 files changed, 991 insertions(+), 12 deletions(-) create mode 100644 test/clone.cjs create mode 100644 test/clone.mjs create mode 100644 test/makeCallable.cjs create mode 100644 test/makeCallable.mjs rename test/{test.cjs => newCallable.cjs} (89%) rename test/{test.mjs => newCallable.mjs} (92%) rename test/{overrideCallProperties.ts => overrideCall.ts} (100%) diff --git a/lib/index.d.ts b/lib/index.d.ts index 9e7445f..d2a7b2c 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -22,7 +22,6 @@ declare module "callable-instance" { */ (...args: Parameters[P]>): ReturnType[P]>; } - type ExtractFunc< C extends BaseClass | BaseFunc | BaseInterface, P extends BaseProperty @@ -35,7 +34,17 @@ declare module "callable-instance" { : never; export interface CallableConstructor { - readonly CALL: SCALL; + get CALL(): SCALL; + + makeCallable( + object: I, + property: P + ): I & ExtractFuncFromInterface; + makeCallable( + object: I + ): I & ExtractFuncFromInterface; + + clone(callableObject: C): C; new < C extends BaseClass | BaseFunc | BaseInterface, diff --git a/lib/index.js b/lib/index.js index 97e44df..5a82b02 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,29 +1,69 @@ "use strict"; const CALL = Symbol("Callable.CALL"); -const BOUND = Symbol("Callable.BOUND"); +const PROPERTY = Symbol("Callable.PROPERTY"); + +function CallableFunction(...args) { + return this.bound[this.bound[PROPERTY]](...args); +} + class Callable extends Function { static get CALL() { return CALL; } + get [Symbol.toStringTag]() { return "Callable"; } + constructor(property) { - super("...a", "return this.a[this.b][this.c](...a)"); + super("...a", "return this(...a);"); if (property === undefined) { property = CALL; } - this[BOUND] = this.bind({ - a: this, - b: BOUND, - c: property, + this.func = CallableFunction.bind(this); + this.bound = this.bind(this.func); + Object.defineProperty(this.bound, PROPERTY, { + writeable: false, + enumerable: false, + configurable: false, + value: property, }); Object.defineProperty( - this[BOUND], + this.bound, "name", Object.getOwnPropertyDescriptor(this.constructor, "name") ); - return this[BOUND]; + return this.bound; + } + + static makeCallable(obj, property) { + if (obj !== undefined && obj !== null) { + const prototype = Object.getPrototypeOf(obj); + if (prototype === Object.prototype || prototype === Callable.prototype) { + const callableObject = new Callable(property); + Object.defineProperties( + callableObject, + Object.getOwnPropertyDescriptors(obj) + ); + return callableObject; + } + } + throw new TypeError( + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ); + } + + static clone(obj) { + if ( + obj !== undefined && + obj !== null && + Object.getPrototypeOf(obj) === Callable.prototype + ) { + return Callable.makeCallable(obj, obj[PROPERTY]); + } + throw new TypeError( + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ); } } diff --git a/test/clone.cjs b/test/clone.cjs new file mode 100644 index 0000000..343bd1e --- /dev/null +++ b/test/clone.cjs @@ -0,0 +1,239 @@ +const assert = require("assert"); +const Callable = require("callable-instance"); + +function getTitle(name, isDefault) { + return `Callable.clone() ${name}${isDefault ? " default" : ""} (mjs)`; +} + +function isCloned(obj1, obj2) { + const obj1Descriptors = Object.entries( + Object.getOwnPropertyDescriptors(obj1) + ); + const obj2Descriptors = Object.entries( + Object.getOwnPropertyDescriptors(obj2) + ); + const obj2DescriptorsObj = Object.getOwnPropertyDescriptors(obj2); + + if (obj1 === obj2 || obj1Descriptors.length !== obj2Descriptors.length) { + return false; + } + if (Object.getPrototypeOf(obj1) !== Object.getPrototypeOf(obj2)) { + return false; + } + if ( + obj1Descriptors.every(([key, value]) => { + if (obj2DescriptorsObj[key]) { + if (obj2DescriptorsObj[key].enumerable === value.enumerable) { + if (obj2DescriptorsObj[key].writable === value.writable) { + if (obj2DescriptorsObj[key].configurable === value.configurable) { + if (obj2DescriptorsObj[key].value === value.value) { + return true; + } + } + } + } + } + return false; + }) + ) { + return true; + } + return false; +} + +const cloneCallable = (source) => Callable.clone(source); + +function defaultTest(clone, source, name, prototypes = []) { + return describe(getTitle(name, true), function () { + it("is callable", function () { + assert(typeof clone(source) === "function"); + }); + it("correctly inherits prototypes", function () { + assert(typeof clone(source) === "function"); + const defaultPrototypes = [Object, Function, Callable]; + for (let i = 0; i !== defaultPrototypes.length; i++) { + assert(clone(source) instanceof defaultPrototypes[i]); + } + for (let i = 0; i !== prototypes.length; i++) { + assert(clone(source) instanceof defaultPrototypes[i]); + } + }); + it("is deepequal to source", function () { + assert(isCloned(clone(source), source)); + }); + it("copies name property from constructor", function () { + assert(clone(source).name === "Callable"); + }); + it("has length property set to 0 due to ...args", function () { + assert(clone(source).length === 0); + }); + it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { + assert(clone(source).func === undefined); + assert(clone(source).bound === undefined); + assert( + Object.getOwnPropertyDescriptor(clone(source), "func") === undefined + ); + assert( + Object.getOwnPropertyDescriptor(clone(source), "bound") === undefined + ); + }); + }); +} + +defaultTest(cloneCallable, new Callable("test"), "clone of regular callable"); + +defaultTest( + cloneCallable, + Callable.makeCallable({ + test: "test", + [Callable.CALL]() { + return this.test; + }, + }), + "clone of regular callable" +); + +class CallableChild extends Callable { + constructor() { + super(); + } +} + +describe( + getTitle( + "clone CallableChild | Array | Object | null | undefined | Function etc", + true + ), + function () { + it("must throw on creation", function () { + assert( + (() => { + try { + Callable.clone(new CallableChild()); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone({}); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone(null); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone(undefined); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone([]); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone(function () {}); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone(23); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone("123"); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + }); + } +); diff --git a/test/clone.mjs b/test/clone.mjs new file mode 100644 index 0000000..fafe398 --- /dev/null +++ b/test/clone.mjs @@ -0,0 +1,239 @@ +import assert from "assert"; +import Callable from "callable-instance"; + +function getTitle(name, isDefault) { + return `Callable.clone() ${name}${isDefault ? " default" : ""} (mjs)`; +} + +function isCloned(obj1, obj2) { + const obj1Descriptors = Object.entries( + Object.getOwnPropertyDescriptors(obj1) + ); + const obj2Descriptors = Object.entries( + Object.getOwnPropertyDescriptors(obj2) + ); + const obj2DescriptorsObj = Object.getOwnPropertyDescriptors(obj2); + + if (obj1 === obj2 || obj1Descriptors.length !== obj2Descriptors.length) { + return false; + } + if (Object.getPrototypeOf(obj1) !== Object.getPrototypeOf(obj2)) { + return false; + } + if ( + obj1Descriptors.every(([key, value]) => { + if (obj2DescriptorsObj[key]) { + if (obj2DescriptorsObj[key].enumerable === value.enumerable) { + if (obj2DescriptorsObj[key].writable === value.writable) { + if (obj2DescriptorsObj[key].configurable === value.configurable) { + if (obj2DescriptorsObj[key].value === value.value) { + return true; + } + } + } + } + } + return false; + }) + ) { + return true; + } + return false; +} + +const cloneCallable = (source) => Callable.clone(source); + +function defaultTest(clone, source, name, prototypes = []) { + return describe(getTitle(name, true), function () { + it("is callable", function () { + assert(typeof clone(source) === "function"); + }); + it("correctly inherits prototypes", function () { + assert(typeof clone(source) === "function"); + const defaultPrototypes = [Object, Function, Callable]; + for (let i = 0; i !== defaultPrototypes.length; i++) { + assert(clone(source) instanceof defaultPrototypes[i]); + } + for (let i = 0; i !== prototypes.length; i++) { + assert(clone(source) instanceof defaultPrototypes[i]); + } + }); + it("is deepequal to source", function () { + assert(isCloned(clone(source), source)); + }); + it("copies name property from constructor", function () { + assert(clone(source).name === "Callable"); + }); + it("has length property set to 0 due to ...args", function () { + assert(clone(source).length === 0); + }); + it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { + assert(clone(source).func === undefined); + assert(clone(source).bound === undefined); + assert( + Object.getOwnPropertyDescriptor(clone(source), "func") === undefined + ); + assert( + Object.getOwnPropertyDescriptor(clone(source), "bound") === undefined + ); + }); + }); +} + +defaultTest(cloneCallable, new Callable("test"), "clone of regular callable"); + +defaultTest( + cloneCallable, + Callable.makeCallable({ + test: "test", + [Callable.CALL]() { + return this.test; + }, + }), + "clone of regular callable" +); + +class CallableChild extends Callable { + constructor() { + super(); + } +} + +describe( + getTitle( + "clone CallableChild | Array | Object | null | undefined | Function etc", + true + ), + function () { + it("must throw on creation", function () { + assert( + (() => { + try { + Callable.clone(new CallableChild()); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone({}); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone(null); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone(undefined); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone([]); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone(function () {}); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone(23); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.clone("123"); + return false; + } catch (e) { + if ( + e.message === + "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + ) { + return true; + } + return false; + } + })() + ); + }); + } +); diff --git a/test/makeCallable.cjs b/test/makeCallable.cjs new file mode 100644 index 0000000..f1cf05f --- /dev/null +++ b/test/makeCallable.cjs @@ -0,0 +1,212 @@ +const assert = require("assert"); +const Callable = require("callable-instance"); + +function getTitle(name, isDefault) { + return `Callable.makeCallable() ${name}${isDefault ? " default" : ""} (cjs)`; +} + +const createRegular = () => + Callable.makeCallable({ + test: "test", + [Callable.CALL]() { + return this.test; + }, + }); + +function defaultTest(create, name, prototypes = []) { + return describe(getTitle(name, true), function () { + it("is callable", function () { + assert(typeof create() === "function"); + }); + it("correctly inherits prototypes", function () { + assert(typeof create() === "function"); + const defaultPrototypes = [Object, Function, Callable]; + for (let i = 0; i !== defaultPrototypes.length; i++) { + assert(create() instanceof defaultPrototypes[i]); + } + for (let i = 0; i !== prototypes.length; i++) { + assert(create() instanceof defaultPrototypes[i]); + } + }); + it("copies name property from constructor", function () { + assert(create().name === "Callable"); + }); + it("has length property set to 0 due to ...args", function () { + assert(create().length === 0); + }); + it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { + assert(create().func === undefined); + assert(create().bound === undefined); + assert(Object.getOwnPropertyDescriptor(create(), "func") === undefined); + assert(Object.getOwnPropertyDescriptor(create(), "bound") === undefined); + }); + }); +} + +defaultTest(createRegular, "creation from regular object"); + +const createFromCallable = () => createRegular(createRegular()); + +defaultTest(createFromCallable, "creation from Callable"); + +class CallableChild extends Callable { + constructor() { + super(); + } + [Callable.CALL]() { + return "CallableChildCall"; + } +} + +const createFromCallableChild = () => + Callable.makeCallable(new CallableChild()); + +class ObjectChild extends Object { + constructor() { + super(); + } + [Callable.CALL]() { + return "ObjectChildCall"; + } +} + +const createFromObjectChild = () => Callable.makeCallable(new ObjectChild()); + +describe( + getTitle( + "makeCallable from CallableChild | Array | ObjectChild | null | undefined | Function etc", + true + ), + function () { + it("must throw on creation", function () { + assert( + (() => { + try { + createFromCallableChild(); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + createFromObjectChild(); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable(null); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable(undefined); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable([]); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable(function () {}); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable(23); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable("123"); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + }); + } +); diff --git a/test/makeCallable.mjs b/test/makeCallable.mjs new file mode 100644 index 0000000..501dadb --- /dev/null +++ b/test/makeCallable.mjs @@ -0,0 +1,212 @@ +import assert from "assert"; +import Callable from "callable-instance"; + +function getTitle(name, isDefault) { + return `Callable.makeCallable() ${name}${isDefault ? " default" : ""} (mjs)`; +} + +const createRegular = () => + Callable.makeCallable({ + test: "test", + [Callable.CALL]() { + return this.test; + }, + }); + +function defaultTest(create, name, prototypes = []) { + return describe(getTitle(name, true), function () { + it("is callable", function () { + assert(typeof create() === "function"); + }); + it("correctly inherits prototypes", function () { + assert(typeof create() === "function"); + const defaultPrototypes = [Object, Function, Callable]; + for (let i = 0; i !== defaultPrototypes.length; i++) { + assert(create() instanceof defaultPrototypes[i]); + } + for (let i = 0; i !== prototypes.length; i++) { + assert(create() instanceof defaultPrototypes[i]); + } + }); + it("copies name property from constructor", function () { + assert(create().name === "Callable"); + }); + it("has length property set to 0 due to ...args", function () { + assert(create().length === 0); + }); + it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { + assert(create().func === undefined); + assert(create().bound === undefined); + assert(Object.getOwnPropertyDescriptor(create(), "func") === undefined); + assert(Object.getOwnPropertyDescriptor(create(), "bound") === undefined); + }); + }); +} + +defaultTest(createRegular, "creation from regular object"); + +const createFromCallable = () => createRegular(createRegular()); + +defaultTest(createFromCallable, "creation from Callable"); + +class CallableChild extends Callable { + constructor() { + super(); + } + [Callable.CALL]() { + return "CallableChildCall"; + } +} + +const createFromCallableChild = () => + Callable.makeCallable(new CallableChild()); + +class ObjectChild extends Object { + constructor() { + super(); + } + [Callable.CALL]() { + return "ObjectChildCall"; + } +} + +const createFromObjectChild = () => Callable.makeCallable(new ObjectChild()); + +describe( + getTitle( + "makeCallable from CallableChild | Array | ObjectChild | null | undefined | Function etc", + true + ), + function () { + it("must throw on creation", function () { + assert( + (() => { + try { + createFromCallableChild(); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + createFromObjectChild(); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable(null); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable(undefined); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable([]); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable(function () {}); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable(23); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + assert( + (() => { + try { + Callable.makeCallable("123"); + return false; + } catch (e) { + if ( + e.message === + "Callable.makeCallable accepts only regular object or direct instance of Callable" + ) { + return true; + } + return false; + } + })() + ); + }); + } +); diff --git a/test/test.cjs b/test/newCallable.cjs similarity index 89% rename from test/test.cjs rename to test/newCallable.cjs index 582dc6d..72b76a6 100644 --- a/test/test.cjs +++ b/test/newCallable.cjs @@ -34,6 +34,18 @@ function defaultTest(Class, prototypes = []) { it("has length property set to 0 due to ...args", function () { assert(new Class("testing").length === 0); }); + it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { + assert(new Class("testing").func === undefined); + assert(new Class("testing").bound === undefined); + assert( + Object.getOwnPropertyDescriptor(new Class("testing"), "func") === + undefined + ); + assert( + Object.getOwnPropertyDescriptor(new Class("testing"), "bound") === + undefined + ); + }); }); } @@ -63,7 +75,9 @@ describe(getTitle(MyTest), function () { assert(test.go() === "new message"); }); it("has own string tag C", function () { - assert(Object.prototype.toString.call(new MyTest("test")) === "[object Callable]"); + assert( + Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" + ); }); it("supports property redefine", function () { const obj = new MyTest("testing"); @@ -106,7 +120,9 @@ describe(getTitle(MyTestExtended), function () { assert(test.go() === "new message"); }); it("has own string tag C", function () { - assert(Object.prototype.toString.call(new MyTest("test")) === "[object Callable]"); + assert( + Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" + ); }); it("supports property redefine", function () { const obj = new MyTestExtended("testing"); diff --git a/test/test.mjs b/test/newCallable.mjs similarity index 92% rename from test/test.mjs rename to test/newCallable.mjs index 1e711f4..7f8db9a 100644 --- a/test/test.mjs +++ b/test/newCallable.mjs @@ -34,6 +34,18 @@ function defaultTest(Class, prototypes = []) { it("has length property set to 0 due to ...args", function () { assert(new Class("testing").length === 0); }); + it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { + assert(new Class("testing").func === undefined); + assert(new Class("testing").bound === undefined); + assert( + Object.getOwnPropertyDescriptor(new Class("testing"), "func") === + undefined + ); + assert( + Object.getOwnPropertyDescriptor(new Class("testing"), "bound") === + undefined + ); + }); }); } diff --git a/test/overrideCallProperties.ts b/test/overrideCall.ts similarity index 100% rename from test/overrideCallProperties.ts rename to test/overrideCall.ts From 584213dcd88f3fb2a6273fe44bcd94c34a6de55f Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 00:10:17 +0200 Subject: [PATCH 19/60] Update README.md --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index d14ba15..ef2d966 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,40 @@ class ExampleClassWithCustomMethodName extends Callable { } ``` +### Other Usage Variant +If you prefer to use regular JS objects, you can easily make them callable. + +```javascript +import Callable from "callable-instance"; +const callableObject = Callable.makeCallable({ + test: "test", + [Callable.CALL]() { + return this.test; + }, +}); + +// if you prefer to use spread operator, for callable it will be similar. +const cloned = Callable.makeCallable({...callableObject}); + +// or you can use Callable.clone (accepts only direct instance of Callable. e.g. made with makeCallable) +const cloned2 = Callable.clone(callableObject); +``` +> **_NOTE:_** Usage of custom method name is also supported. +```javascript +import Callable from "callable-instance"; +const callableObject = Callable.makeCallable({ + test: "test", + getTest() { + return this.test; + }, +}, "getTest"); + +// but you will need to provide method name when cloning using makeCallable +const cloned = Callable.makeCallable({...callableObject}, "getTest"); + +// clone does not have this issue +const cloned2 = Callable.clone(callableObject); +``` ## Typescript From fe8d72430a0f6f8da4058d65ce16a87e433c939f Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 00:10:52 +0200 Subject: [PATCH 20/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef2d966..b5a61ae 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ class ExampleClassWithCustomMethodName extends Callable { ``` ### Other Usage Variant -If you prefer to use regular JS objects, you can easily make them callable. +If you prefer using regular JS objects, you can easily make them callable. ```javascript import Callable from "callable-instance"; From c96f6901d54f070cf1069eba91c8277647a0ee53 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 00:13:47 +0200 Subject: [PATCH 21/60] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b5a61ae..33664a5 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ class ExampleClassWithCustomMethodName extends Callable { ``` ### Other Usage Variant -If you prefer using regular JS objects, you can easily make them callable. +Making regular JS objects callable. ```javascript import Callable from "callable-instance"; @@ -62,10 +62,10 @@ const callableObject = Callable.makeCallable({ }, }); -// if you prefer to use spread operator, for callable it will be similar. +// cloning using spread operator. (spread looses call signature so it is important to call makeCallable again) const cloned = Callable.makeCallable({...callableObject}); -// or you can use Callable.clone (accepts only direct instance of Callable. e.g. made with makeCallable) +// cloning using Callable.clone way (accepts only direct instance of Callable. e.g. made with makeCallable) const cloned2 = Callable.clone(callableObject); ``` > **_NOTE:_** Usage of custom method name is also supported. @@ -78,10 +78,10 @@ const callableObject = Callable.makeCallable({ }, }, "getTest"); -// but you will need to provide method name when cloning using makeCallable +// but it is important to provide method name when cloning using makeCallable const cloned = Callable.makeCallable({...callableObject}, "getTest"); -// clone does not have this issue +// Callable.clone does not have this issue const cloned2 = Callable.clone(callableObject); ``` From e9996bc2b93e4b233decd43e6bd3580d55228316 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 00:17:11 +0200 Subject: [PATCH 22/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33664a5..89fd270 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ class ExampleClassWithCustomMethodName extends Callable { ``` ### Other Usage Variant -Making regular JS objects callable. +In the next example, we will create `callableObject`. `Callable.makeCallable` will add call signature to object. ```javascript import Callable from "callable-instance"; From a1cd3463f01d46e4ee58d5f99b4672486785c25f Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 00:38:27 +0200 Subject: [PATCH 23/60] Update README.md --- README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 89fd270..80ce18c 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ test.apply(null, [1, 2, 3]); ```javascript class ExampleClassWithCustomMethodName extends Callable { constructor(){ + // in super enter object method`s name which will be used when calling object super('myMethod') } myMethod(arg){ @@ -55,6 +56,7 @@ In the next example, we will create `callableObject`. `Callable.makeCallable` wi ```javascript import Callable from "callable-instance"; +// makeCallable adds call signature to object const callableObject = Callable.makeCallable({ test: "test", [Callable.CALL]() { @@ -65,7 +67,7 @@ const callableObject = Callable.makeCallable({ // cloning using spread operator. (spread looses call signature so it is important to call makeCallable again) const cloned = Callable.makeCallable({...callableObject}); -// cloning using Callable.clone way (accepts only direct instance of Callable. e.g. made with makeCallable) +// cloning using Callable.clone (accepts only direct instance of Callable. e.g. made with makeCallable) const cloned2 = Callable.clone(callableObject); ``` > **_NOTE:_** Usage of custom method name is also supported. @@ -76,9 +78,9 @@ const callableObject = Callable.makeCallable({ getTest() { return this.test; }, -}, "getTest"); +}, "getTest"); // second parameter is optional method`s name which will be used when calling object -// but it is important to provide method name when cloning using makeCallable +// but it is important to also provide method name when cloning using spread + makeCallable const cloned = Callable.makeCallable({...callableObject}, "getTest"); // Callable.clone does not have this issue @@ -95,12 +97,13 @@ const cloned2 = Callable.clone(callableObject); ```typescript // Callable has 2 generics // 1st is for class | interface | function (for extracting type of call signature) -// 2nd optional generic is for propertyName (string | symbol | number). defaults to Callable.CALL +// 2nd optional generic is for method name (string | symbol | number) which will be used as type of call signature from 1st generic (defaults to Callable.CALL) interface IExampleClass { + // interface type will provide actual type of the function without limits [Callable.CALL](arg: A): A } - +// implements is optional but advised https://www.typescriptlang.org/docs/handbook/interfaces.html class ExampleClass extends Callable implements IExampleClass { constructor(){ super() @@ -125,6 +128,7 @@ class ExampleClass extends Callable<(arg: A) => A> { 3. **Using class type** ```typescript +// easiest way of typing Callable class ExampleClass extends Callable { constructor(){ super() @@ -148,6 +152,7 @@ It can be used to override call signature in child classes. // 2nd generic is Child. Can be interface | class | function // 3rd optional generic is propertyName can be string | symbol | number. defaults to Callable.CALL +// call signature is (() => string) class ExampleClass extends Callable<() => string> { constructor() { super(); @@ -157,6 +162,7 @@ class ExampleClass extends Callable<() => string> { } } +// overriding call signature to (() => number) class ExampleClassChild extends (ExampleClass as OverrideCall)<() => number> { constructor(){ super(); @@ -172,6 +178,8 @@ class ExampleClassChild extends (ExampleClass as OverrideCall test.methodName(...a)` + Libraries that accept functions will expect that they behave as Function objects do. For example, if you alter the semantics of the `call` or `apply` methods, library code may fail to work with your callable instance. In these cases, you can simply bind the instance method to the callable instance and pass that instead (e.g. `test.instanceMethod.bind(test)`). This can also cause problems if your derived class wants to have a `name` or `length` property, which are built-in properties and not configurable by default. You can have your class disable the built-in descriptors of these properties to make them available for your use. From da397438f606e8fac999c5d07647a4a7d0d53bc7 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 00:43:28 +0200 Subject: [PATCH 24/60] Update README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 80ce18c..75696f1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://img.shields.io/github/actions/workflow/status/CGamesPlay/node-callable-instance/node.js.yml?branch=master)](https://github.com/CGamesPlay/node-callable-instance/actions/workflows/node.js.yml) [![Download Size](https://img.shields.io/bundlephobia/min/callable-instance.svg?style=flat)](https://bundlephobia.com/package/callable-instance@latest) [![dependencies](https://img.shields.io/badge/dependencies-none-brightgreen)](https://www.npmjs.com/package/callable-instance?activeTab=dependencies) [![npm](https://img.shields.io/npm/v/callable-instance)](https://www.npmjs.com/package/callable-instance) [![npm](https://img.shields.io/npm/dw/callable-instance)](https://www.npmjs.com/package/callable-instance) -This module allows you to create an ES6 class that is callable as a function. The invocation is sent to one of the object's normal prototype methods. +This module allows you to create an ES6 `class` or regular JS `Object` that is callable as a function. ## Installation @@ -178,9 +178,7 @@ class ExampleClassChild extends (ExampleClass as OverrideCall test.methodName(...a)` - -Libraries that accept functions will expect that they behave as Function objects do. For example, if you alter the semantics of the `call` or `apply` methods, library code may fail to work with your callable instance. In these cases, you can simply bind the instance method to the callable instance and pass that instead (e.g. `test.instanceMethod.bind(test)`). +Call signature behaves exactly like the method specified in constructor but they are not equal. `Callable` just creates an alias to the function and it's `this` value must always be function. So it is not advised to use `call`, `apply` or `bind` with `Callable` directly. Consider using `test.instanceMethod.call(test2, ...args)` intead of `test.call(test2, ...args)`. This can also cause problems if your derived class wants to have a `name` or `length` property, which are built-in properties and not configurable by default. You can have your class disable the built-in descriptors of these properties to make them available for your use. From 50b89aac3a5863d6561cbaa4787151108b1ad544 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 00:47:47 +0200 Subject: [PATCH 25/60] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 75696f1..910ad8c 100644 --- a/README.md +++ b/README.md @@ -178,9 +178,9 @@ class ExampleClassChild extends (ExampleClass as OverrideCall Date: Fri, 27 Oct 2023 00:55:07 +0200 Subject: [PATCH 26/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 910ad8c..cce6f5d 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ class ExampleClassChild extends (ExampleClass as OverrideCall Date: Fri, 27 Oct 2023 00:59:54 +0200 Subject: [PATCH 27/60] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index cce6f5d..61e6bc7 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,11 @@ class ExampleClassWithCustomMethodName extends Callable { ``` ### Other Usage Variant -In the next example, we will create `callableObject`. `Callable.makeCallable` will add call signature to object. +In the next example, we will create `callableObject`. using Callable.makeCallable ```javascript import Callable from "callable-instance"; -// makeCallable adds call signature to object +// makeCallable creates new callable object with all the properties from source object const callableObject = Callable.makeCallable({ test: "test", [Callable.CALL]() { @@ -64,7 +64,7 @@ const callableObject = Callable.makeCallable({ }, }); -// cloning using spread operator. (spread looses call signature so it is important to call makeCallable again) +// cloning using spread operator + makeCallable. (spread looses call signature so it is important to call makeCallable again) const cloned = Callable.makeCallable({...callableObject}); // cloning using Callable.clone (accepts only direct instance of Callable. e.g. made with makeCallable) @@ -101,14 +101,14 @@ const cloned2 = Callable.clone(callableObject); interface IExampleClass { // interface type will provide actual type of the function without limits - [Callable.CALL](arg: A): A + [Callable.CALL](arg: string): string } // implements is optional but advised https://www.typescriptlang.org/docs/handbook/interfaces.html class ExampleClass extends Callable implements IExampleClass { constructor(){ super() } - [Callable.CALL](arg: A){ + [Callable.CALL](arg: string){ return arg } } @@ -116,11 +116,11 @@ class ExampleClass extends Callable implements IExampleClass { 2. **Using function type** ```typescript -class ExampleClass extends Callable<(arg: A) => A> { +class ExampleClass extends Callable<(arg: string) => string> { constructor(){ super() } - [Callable.CALL](arg: A){ + [Callable.CALL](arg: string){ return arg } } @@ -128,7 +128,7 @@ class ExampleClass extends Callable<(arg: A) => A> { 3. **Using class type** ```typescript -// easiest way of typing Callable +// easiest way for typing Callable class ExampleClass extends Callable { constructor(){ super() From 26a5acd63a9710a68bc8787af65f287e24b79200 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 01:00:28 +0200 Subject: [PATCH 28/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61e6bc7..0b119dd 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ test.apply(null, [1, 2, 3]); ```javascript class ExampleClassWithCustomMethodName extends Callable { constructor(){ - // in super enter object method`s name which will be used when calling object + // in super provide object method`s name which will be used when calling object super('myMethod') } myMethod(arg){ From 354bb2226e3b0df66dd943e8c73fce0c792c22de Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 01:01:48 +0200 Subject: [PATCH 29/60] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 0b119dd..e82514c 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,6 @@ var test = new ExampleClass(); test[Callable.CALL](); // Call the instance itself, redirects to instanceMethod test(); -// The instance is actually a closure bound to itself and can be used like a -// normal function. -test.apply(null, [1, 2, 3]); ``` > **_NOTE:_** Usage of custom method name is also supported. From 6b0475c4871406fc9a2a4a972e82da5454424533 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 01:02:56 +0200 Subject: [PATCH 30/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e82514c..d1153df 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ class ExampleClassWithCustomMethodName extends Callable { ``` ### Other Usage Variant -In the next example, we will create `callableObject`. using Callable.makeCallable +In the next example, we will create `callableObject` using Callable.makeCallable. ```javascript import Callable from "callable-instance"; From d6727b9e973692be132ed472d0cdd219ebb1cd77 Mon Sep 17 00:00:00 2001 From: pabadm Date: Fri, 27 Oct 2023 21:16:33 +0200 Subject: [PATCH 31/60] created aliases to bind,call,apply --- lib/index.js | 54 +++++++++++++++++++++++++++++++--------- test/interfaceGeneric.ts | 22 ++++++++++------ test/newCallable.cjs | 30 +++++++++++++++++----- test/newCallable.mjs | 34 +++++++++++++++++++------ 4 files changed, 107 insertions(+), 33 deletions(-) diff --git a/lib/index.js b/lib/index.js index 5a82b02..3c3eeac 100644 --- a/lib/index.js +++ b/lib/index.js @@ -6,6 +6,26 @@ function CallableFunction(...args) { return this.bound[this.bound[PROPERTY]](...args); } +const privateValueDescriptor = { + writeable: false, + enumerable: false, + configurable: false, +}; + +const descriptors = Object.getOwnPropertyDescriptors(Function.prototype); + +function callableBind(t) { + return this.bind(this.bound[this.bound[PROPERTY]].bind(t)); +} + +function callableApply(t, args) { + return this.bound[this.bound[PROPERTY]].apply(t, args); +} + +function callableCall(t, ...args) { + return this.bound[this.bound[PROPERTY]].call(t, ...args); +} + class Callable extends Function { static get CALL() { return CALL; @@ -20,19 +40,29 @@ class Callable extends Function { if (property === undefined) { property = CALL; } - this.func = CallableFunction.bind(this); - this.bound = this.bind(this.func); - Object.defineProperty(this.bound, PROPERTY, { - writeable: false, - enumerable: false, - configurable: false, - value: property, + + this.bound = this.bind(CallableFunction.bind(this)); + + Object.defineProperties(this.bound, { + [PROPERTY]: { + ...privateValueDescriptor, + value: property, + }, + name: Object.getOwnPropertyDescriptor(this.constructor, "name"), + apply: { + ...descriptors.apply, + value: callableApply.bind(this), + }, + call: { + ...descriptors.call, + value: callableCall.bind(this), + }, + bind: { + ...descriptors.bind, + value: callableBind.bind(this), + }, }); - Object.defineProperty( - this.bound, - "name", - Object.getOwnPropertyDescriptor(this.constructor, "name") - ); + return this.bound; } diff --git a/test/interfaceGeneric.ts b/test/interfaceGeneric.ts index 23ecabd..8b2d0cd 100644 --- a/test/interfaceGeneric.ts +++ b/test/interfaceGeneric.ts @@ -171,17 +171,19 @@ class RepeaterWithGenericInterface implements IGenericInterface { constructor() { - super('go'); + super("go"); } - public count = 23 + public count = 23; go(g) { - return g + return g; } } describe("Callable With Generic Interface Generic and custom property (TypeScript)", function () { it("is callable", function () { - expectType<(arg: G) => G>(new RepeaterWithGenericInterface()); + expectType<(arg: G) => G>( + new RepeaterWithGenericInterface() + ); new RepeaterWithGenericInterface()("testing"); // @ts-expect-error wrong type for method new RepeaterWithGenericInterface()(5).go(5); @@ -193,9 +195,15 @@ describe("Callable With Generic Interface Generic and custom property (TypeScrip expectType( new RepeaterWithGenericInterface() ); - expectType<(arg: G) => G>(new RepeaterWithGenericInterface().go); - expectType<(arg: G) => G>(new RepeaterWithGenericInterface()); - expectType<(arg: G) => G>(new RepeaterWithGenericInterface().go); + expectType<(arg: G) => G>( + new RepeaterWithGenericInterface().go + ); + expectType<(arg: G) => G>( + new RepeaterWithGenericInterface() + ); + expectType<(arg: G) => G>( + new RepeaterWithGenericInterface().go + ); }); it("is an instance of Repeater", function () { diff --git a/test/newCallable.cjs b/test/newCallable.cjs index 72b76a6..ca29d28 100644 --- a/test/newCallable.cjs +++ b/test/newCallable.cjs @@ -28,19 +28,37 @@ function defaultTest(Class, prototypes = []) { assert(new Class("msg") instanceof defaultPrototypes[i]); } }); + it("it's bind method correctly inherits prototypes", function () { + assert(typeof new Class("msg").bind({}) === "function"); + const defaultPrototypes = [Object, Function, Callable, Class]; + for (let i = 0; i !== defaultPrototypes.length; i++) { + assert(new Class("msg").bind({}) instanceof defaultPrototypes[i]); + } + for (let i = 0; i !== prototypes.length; i++) { + assert(new Class("msg").bind({}) instanceof defaultPrototypes[i]); + } + }); + it("it's call, apply method is overriden and works exactly like [CALL].apply, [CALL].call", function () { + assert(typeof new Class("msg").call === "function"); + assert(typeof new Class("msg").apply === "function"); + const defaultPrototypes = [Object, Function]; + for (let i = 0; i !== defaultPrototypes.length; i++) { + assert(new Class("msg").call instanceof defaultPrototypes[i]); + assert(new Class("msg").apply instanceof defaultPrototypes[i]); + } + for (let i = 0; i !== prototypes.length; i++) { + assert(new Class("msg").call instanceof defaultPrototypes[i]); + assert(new Class("msg").apply instanceof defaultPrototypes[i]); + } + }); it("copies name property from constructor", function () { assert(new Class("test").name === Class.name); }); it("has length property set to 0 due to ...args", function () { assert(new Class("testing").length === 0); }); - it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { - assert(new Class("testing").func === undefined); + it("has not accessible properties bound (because it returns this.bound instead of this)", function () { assert(new Class("testing").bound === undefined); - assert( - Object.getOwnPropertyDescriptor(new Class("testing"), "func") === - undefined - ); assert( Object.getOwnPropertyDescriptor(new Class("testing"), "bound") === undefined diff --git a/test/newCallable.mjs b/test/newCallable.mjs index 7f8db9a..5e213fa 100644 --- a/test/newCallable.mjs +++ b/test/newCallable.mjs @@ -28,19 +28,37 @@ function defaultTest(Class, prototypes = []) { assert(new Class("msg") instanceof defaultPrototypes[i]); } }); + it("it's bind method correctly inherits prototypes", function () { + assert(typeof new Class("msg").bind({}) === "function"); + const defaultPrototypes = [Object, Function, Callable, Class]; + for (let i = 0; i !== defaultPrototypes.length; i++) { + assert(new Class("msg").bind({}) instanceof defaultPrototypes[i]); + } + for (let i = 0; i !== prototypes.length; i++) { + assert(new Class("msg").bind({}) instanceof defaultPrototypes[i]); + } + }); + it("it's call, apply method is overriden and works exactly like [CALL].apply, [CALL].call", function () { + assert(typeof new Class("msg").call === "function"); + assert(typeof new Class("msg").apply === "function"); + const defaultPrototypes = [Object, Function]; + for (let i = 0; i !== defaultPrototypes.length; i++) { + assert(new Class("msg").call instanceof defaultPrototypes[i]); + assert(new Class("msg").apply instanceof defaultPrototypes[i]); + } + for (let i = 0; i !== prototypes.length; i++) { + assert(new Class("msg").call instanceof defaultPrototypes[i]); + assert(new Class("msg").apply instanceof defaultPrototypes[i]); + } + }); it("copies name property from constructor", function () { assert(new Class("test").name === Class.name); }); it("has length property set to 0 due to ...args", function () { assert(new Class("testing").length === 0); }); - it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { - assert(new Class("testing").func === undefined); + it("has not accessible properties bound (because it returns this.bound instead of this)", function () { assert(new Class("testing").bound === undefined); - assert( - Object.getOwnPropertyDescriptor(new Class("testing"), "func") === - undefined - ); assert( Object.getOwnPropertyDescriptor(new Class("testing"), "bound") === undefined @@ -74,7 +92,7 @@ describe(getTitle(MyTest), function () { assert(test() === "new message"); assert(test.go() === "new message"); }); - it("has own string tag Callable", function () { + it("has own string tag C", function () { assert( Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" ); @@ -119,7 +137,7 @@ describe(getTitle(MyTestExtended), function () { assert(test() === "new message"); assert(test.go() === "new message"); }); - it("has own string tag Callable", function () { + it("has own string tag C", function () { assert( Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" ); From 07a5e1c210b214dc504f1b96ab08adfdff5a9882 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 17:52:38 +0200 Subject: [PATCH 32/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1153df..74d4b2d 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ class ExampleClassChild extends (ExampleClass as OverrideCall Date: Fri, 27 Oct 2023 18:28:24 +0200 Subject: [PATCH 33/60] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 74d4b2d..db24269 100644 --- a/README.md +++ b/README.md @@ -181,8 +181,9 @@ Libraries that accept functions will expect that they behave as Function objects ```javascript var test = new ExampleClass(); +console.log(test.name); // Will print 'ExampleClass' (constructor.name is used by default) test.name = "hello!"; -console.log(test.name); // Will print 'instanceMethod' +console.log(test.name); // Will print 'ExampleClass' class NameableClass extends Callable { constructor() { From fef2240270d87a41edeb1cadb692f83ab19fe5b8 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 18:29:34 +0200 Subject: [PATCH 34/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db24269..478253b 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ class ExampleClass extends Callable { var test = new ExampleClass(); // Invoke the method normally test[Callable.CALL](); -// Call the instance itself, redirects to instanceMethod +// Call the instance itself, redirects to [Callable.CALL] test(); ``` > **_NOTE:_** Usage of custom method name is also supported. From f404d78d3c28d556492557f3ed3856b5632c5962 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 18:29:51 +0200 Subject: [PATCH 35/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 478253b..a388f9c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ In the following example, we will create an `ExampleClass` class. The instances ```javascript import Callable from "callable-instance"; // If you aren't using ES modules, you can use require: -// var CallableInstance = require("callable-instance"); +// var Callable = require("callable-instance"); class ExampleClass extends Callable { constructor() { From 34618bf1c92032cfd8a60c01181361e3214a9d16 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Fri, 27 Oct 2023 18:32:38 +0200 Subject: [PATCH 36/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a388f9c..ea56060 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ class ExampleClassChild extends (ExampleClass as OverrideCall Date: Sat, 28 Oct 2023 00:32:31 +0200 Subject: [PATCH 37/60] fixed double bind bug --- lib/index.js | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/index.js b/lib/index.js index 3c3eeac..e19d92f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -15,7 +15,26 @@ const privateValueDescriptor = { const descriptors = Object.getOwnPropertyDescriptors(Function.prototype); function callableBind(t) { - return this.bind(this.bound[this.bound[PROPERTY]].bind(t)); + const bound = this.bind(this.bound[this.bound[PROPERTY]].bind(t)); + Object.defineProperties(bound, { + [PROPERTY]: { + ...privateValueDescriptor, + value: this.bound[PROPERTY], + }, + apply: { + ...descriptors.apply, + value: callableApply.bind(this), + }, + call: { + ...descriptors.call, + value: callableCall.bind(this), + }, + bind: { + ...descriptors.bind, + value: callableBind.bind(this), + }, + }); + return bound; } function callableApply(t, args) { @@ -71,10 +90,9 @@ class Callable extends Function { const prototype = Object.getPrototypeOf(obj); if (prototype === Object.prototype || prototype === Callable.prototype) { const callableObject = new Callable(property); - Object.defineProperties( - callableObject, - Object.getOwnPropertyDescriptors(obj) - ); + const properties = Object.getOwnPropertyDescriptors(obj); + delete properties[PROPERTY]; + Object.defineProperties(callableObject, properties); return callableObject; } } From 3fa75e00d95dc93edac1442d2a70a2a24d52f763 Mon Sep 17 00:00:00 2001 From: pabadm Date: Sat, 28 Oct 2023 00:40:03 +0200 Subject: [PATCH 38/60] fixed call and apply bindings --- lib/index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/index.js b/lib/index.js index e19d92f..f18502b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,7 +2,7 @@ const CALL = Symbol("Callable.CALL"); const PROPERTY = Symbol("Callable.PROPERTY"); -function CallableFunction(...args) { +function callableFunction(...args) { return this.bound[this.bound[PROPERTY]](...args); } @@ -23,11 +23,11 @@ function callableBind(t) { }, apply: { ...descriptors.apply, - value: callableApply.bind(this), + value: callableApply.bind(bound), }, call: { ...descriptors.call, - value: callableCall.bind(this), + value: callableCall.bind(bound), }, bind: { ...descriptors.bind, @@ -38,11 +38,11 @@ function callableBind(t) { } function callableApply(t, args) { - return this.bound[this.bound[PROPERTY]].apply(t, args); + return this[this[PROPERTY]].apply(t, args); } function callableCall(t, ...args) { - return this.bound[this.bound[PROPERTY]].call(t, ...args); + return this[this[PROPERTY]].call(t, ...args); } class Callable extends Function { @@ -60,7 +60,7 @@ class Callable extends Function { property = CALL; } - this.bound = this.bind(CallableFunction.bind(this)); + this.bound = this.bind(callableFunction.bind(this)); Object.defineProperties(this.bound, { [PROPERTY]: { @@ -70,11 +70,11 @@ class Callable extends Function { name: Object.getOwnPropertyDescriptor(this.constructor, "name"), apply: { ...descriptors.apply, - value: callableApply.bind(this), + value: callableApply.bind(this.bound), }, call: { ...descriptors.call, - value: callableCall.bind(this), + value: callableCall.bind(this.bound), }, bind: { ...descriptors.bind, From 95f1d95005b18df8b61d7aa1653135c743ac7d5a Mon Sep 17 00:00:00 2001 From: pabadm Date: Sat, 28 Oct 2023 00:50:58 +0200 Subject: [PATCH 39/60] callableBind now accepts multiple args like function.bind --- lib/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index f18502b..e78322c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -14,8 +14,8 @@ const privateValueDescriptor = { const descriptors = Object.getOwnPropertyDescriptors(Function.prototype); -function callableBind(t) { - const bound = this.bind(this.bound[this.bound[PROPERTY]].bind(t)); +function callableBind(...args) { + const bound = this.bind(this.bound[this.bound[PROPERTY]].bind(...args)); Object.defineProperties(bound, { [PROPERTY]: { ...privateValueDescriptor, From a104b6dcf5057ebe863e54d4c1b9494c302597f6 Mon Sep 17 00:00:00 2001 From: pabadm Date: Sat, 28 Oct 2023 01:12:59 +0200 Subject: [PATCH 40/60] callableApply, callableCall binding fix --- lib/index.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/index.js b/lib/index.js index e78322c..dd0182b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -17,17 +17,13 @@ const descriptors = Object.getOwnPropertyDescriptors(Function.prototype); function callableBind(...args) { const bound = this.bind(this.bound[this.bound[PROPERTY]].bind(...args)); Object.defineProperties(bound, { - [PROPERTY]: { - ...privateValueDescriptor, - value: this.bound[PROPERTY], - }, apply: { ...descriptors.apply, - value: callableApply.bind(bound), + value: callableApply.bind(this.bound), }, call: { ...descriptors.call, - value: callableCall.bind(bound), + value: callableCall.bind(this.bound), }, bind: { ...descriptors.bind, From afe85fa26bfb36d50e33a1a8d475b9b6e2edbf16 Mon Sep 17 00:00:00 2001 From: pabadm Date: Sat, 28 Oct 2023 10:20:55 +0200 Subject: [PATCH 41/60] apply, call, bind logic moved to prototype --- lib/index.d.ts | 4 +-- lib/index.js | 79 +++++++++++++++++++++----------------------------- 2 files changed, 35 insertions(+), 48 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index d2a7b2c..5cdce5f 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -39,10 +39,10 @@ declare module "callable-instance" { makeCallable( object: I, property: P - ): I & ExtractFuncFromInterface; + ): PickProperties & ExtractFuncFromInterface; makeCallable( object: I - ): I & ExtractFuncFromInterface; + ): PickProperties & ExtractFuncFromInterface; clone(callableObject: C): C; diff --git a/lib/index.js b/lib/index.js index dd0182b..fc7e2b7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,6 +1,7 @@ "use strict"; const CALL = Symbol("Callable.CALL"); const PROPERTY = Symbol("Callable.PROPERTY"); +const BOUND_CALL = Symbol("Callable.BOUND"); function callableFunction(...args) { return this.bound[this.bound[PROPERTY]](...args); @@ -12,51 +13,21 @@ const privateValueDescriptor = { configurable: false, }; -const descriptors = Object.getOwnPropertyDescriptors(Function.prototype); - -function callableBind(...args) { - const bound = this.bind(this.bound[this.bound[PROPERTY]].bind(...args)); - Object.defineProperties(bound, { - apply: { - ...descriptors.apply, - value: callableApply.bind(this.bound), - }, - call: { - ...descriptors.call, - value: callableCall.bind(this.bound), - }, - bind: { - ...descriptors.bind, - value: callableBind.bind(this), - }, - }); - return bound; -} - -function callableApply(t, args) { - return this[this[PROPERTY]].apply(t, args); -} - -function callableCall(t, ...args) { - return this[this[PROPERTY]].call(t, ...args); -} - class Callable extends Function { static get CALL() { return CALL; } - get [Symbol.toStringTag]() { - return "Callable"; - } - constructor(property) { super("...a", "return this(...a);"); if (property === undefined) { property = CALL; } - this.bound = this.bind(callableFunction.bind(this)); + this.bound = Function.prototype.bind.call( + this, + callableFunction.bind(this) + ); Object.defineProperties(this.bound, { [PROPERTY]: { @@ -64,18 +35,6 @@ class Callable extends Function { value: property, }, name: Object.getOwnPropertyDescriptor(this.constructor, "name"), - apply: { - ...descriptors.apply, - value: callableApply.bind(this.bound), - }, - call: { - ...descriptors.call, - value: callableCall.bind(this.bound), - }, - bind: { - ...descriptors.bind, - value: callableBind.bind(this), - }, }); return this.bound; @@ -109,6 +68,34 @@ class Callable extends Function { "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" ); } + + get [Symbol.toStringTag]() { + return "Callable"; + } + + apply(t, args) { + return this[this[PROPERTY]].apply(t, args); + } + + call(t, ...args) { + return this[this[PROPERTY]].call(t, ...args); + } + + bind(t, ...args) { + const boundCall = this[this[PROPERTY]].bind(t, ...args); + const bound = Function.prototype.bind.call(this, boundCall); + Object.defineProperties(bound, { + [BOUND_CALL]: { + ...privateValueDescriptor, + value: boundCall, + }, + [PROPERTY]: { + ...privateValueDescriptor, + value: BOUND_CALL, + }, + }); + return bound; + } } module.exports = Callable; From 7512984d71c77eed779b7ad5b5b0eb794d0bb496 Mon Sep 17 00:00:00 2001 From: pabadm Date: Mon, 30 Oct 2023 07:18:49 +0100 Subject: [PATCH 42/60] symbols description edit + methods ...args --- lib/index.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/index.js b/lib/index.js index fc7e2b7..7cb0f79 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,10 +1,11 @@ "use strict"; -const CALL = Symbol("Callable.CALL"); -const PROPERTY = Symbol("Callable.PROPERTY"); -const BOUND_CALL = Symbol("Callable.BOUND"); +const CALL = Symbol("Callable.call"); +const PROPERTY = Symbol("Callable.property"); +const BOUND_CALL = Symbol("Callable.boundCall"); +const BOUND_THIS = Symbol("Callable.boundThis"); function callableFunction(...args) { - return this.bound[this.bound[PROPERTY]](...args); + return this[BOUND_THIS][this[BOUND_THIS][PROPERTY]](...args); } const privateValueDescriptor = { @@ -24,12 +25,12 @@ class Callable extends Function { property = CALL; } - this.bound = Function.prototype.bind.call( + this[BOUND_THIS] = Function.prototype.bind.call( this, callableFunction.bind(this) ); - Object.defineProperties(this.bound, { + Object.defineProperties(this[BOUND_THIS], { [PROPERTY]: { ...privateValueDescriptor, value: property, @@ -37,7 +38,7 @@ class Callable extends Function { name: Object.getOwnPropertyDescriptor(this.constructor, "name"), }); - return this.bound; + return this[BOUND_THIS]; } static makeCallable(obj, property) { @@ -73,16 +74,16 @@ class Callable extends Function { return "Callable"; } - apply(t, args) { - return this[this[PROPERTY]].apply(t, args); + apply(...args) { + return this[this[PROPERTY]].apply(...args); } - call(t, ...args) { - return this[this[PROPERTY]].call(t, ...args); + call(...args) { + return this[this[PROPERTY]].call(...args); } - bind(t, ...args) { - const boundCall = this[this[PROPERTY]].bind(t, ...args); + bind(...args) { + const boundCall = this[this[PROPERTY]].bind(...args); const bound = Function.prototype.bind.call(this, boundCall); Object.defineProperties(bound, { [BOUND_CALL]: { From 03608432a20fb0e0d4f3140046ba33f38e672274 Mon Sep 17 00:00:00 2001 From: pabadm Date: Sun, 3 Dec 2023 20:52:14 +0100 Subject: [PATCH 43/60] reimplementation due to CSP --- .github/workflows/node.js.yml | 25 +++++----- .gitignore | 5 +- lib/index.d.ts | 11 ++--- lib/index.js | 91 +++++++++++++++++------------------ test/classGeneric.ts | 42 ---------------- test/clone.cjs | 16 +++--- test/clone.mjs | 16 +++--- test/funcGeneric.ts | 4 +- test/interfaceGeneric.ts | 2 - test/newCallable.cjs | 13 ++--- test/newCallable.mjs | 13 ++--- test/overrideCall.ts | 4 +- 12 files changed, 85 insertions(+), 157 deletions(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index e176589..df929f0 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -5,27 +5,26 @@ name: Node.js CI on: push: - branches: [ "master" ] + branches: ["master"] pull_request: - branches: [ "master" ] + branches: ["master"] jobs: build: - runs-on: ubuntu-latest strategy: matrix: - node-version: [16.x, 18.x, 19.x, 20.x, 21.x, 22.x, 23.x, 24.x] + node-version: [16.x, 18.x, 19.x, 20.x, 21.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - run: npm ci - - run: npm run build --if-present - - run: npm test + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + - run: npm ci + - run: npm run build --if-present + - run: npm test diff --git a/.gitignore b/.gitignore index eeea8ba..e9a3d6e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,4 @@ jspm_packages .npm # Optional REPL history -.node_repl_history - -# dist -dist \ No newline at end of file +.node_repl_history \ No newline at end of file diff --git a/lib/index.d.ts b/lib/index.d.ts index 5cdce5f..1caca0c 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,9 +1,8 @@ declare module "callable-instance" { const CALL: unique symbol; - export type SCALL = typeof CALL; type BaseProperty = symbol | string | number; - type CustomProperty = Exclude; + type CustomProperty = Exclude; type BaseFunc = (...args: any) => any; type BaseClass = abstract new (...args: any) => any; @@ -34,7 +33,7 @@ declare module "callable-instance" { : never; export interface CallableConstructor { - get CALL(): SCALL; + get CALL(): typeof CALL; makeCallable( object: I, @@ -42,7 +41,7 @@ declare module "callable-instance" { ): PickProperties & ExtractFuncFromInterface; makeCallable( object: I - ): PickProperties & ExtractFuncFromInterface; + ): PickProperties & ExtractFuncFromInterface; clone(callableObject: C): C; @@ -55,7 +54,7 @@ declare module "callable-instance" { new (): ExtractFunc< C, - SCALL + typeof CALL >; } @@ -66,7 +65,7 @@ declare module "callable-instance" { export type OverrideCall = { new < C extends BaseClass | BaseFunc | BaseInterface, - P extends BaseProperty = SCALL + P extends BaseProperty = typeof CALL >( ...args: ConstructorParameters ): Omit>, P> & ExtractFunc; diff --git a/lib/index.js b/lib/index.js index 7cb0f79..1a757f3 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,44 +1,52 @@ "use strict"; const CALL = Symbol("Callable.call"); -const PROPERTY = Symbol("Callable.property"); -const BOUND_CALL = Symbol("Callable.boundCall"); -const BOUND_THIS = Symbol("Callable.boundThis"); +const BOUND = Symbol("Callable.bound"); -function callableFunction(...args) { - return this[BOUND_THIS][this[BOUND_THIS][PROPERTY]](...args); +const PROTOTYPED_FUNCTIONS = new WeakMap(); +const PROPERTIES = new WeakMap(); + +function createFunc() { + return function callableFunction(...args) { + return this[BOUND][PROPERTIES.get(this)](...args); + }; } -const privateValueDescriptor = { - writeable: false, - enumerable: false, - configurable: false, -}; +function CallableConstructor(property) { + if (property === undefined) { + property = CALL; + } -class Callable extends Function { - static get CALL() { - return CALL; + const prototype = this.constructor.prototype; + + let prototypedFunc = PROTOTYPED_FUNCTIONS.get(prototype); + if (prototypedFunc === undefined) { + prototypedFunc = Object.setPrototypeOf(createFunc(), prototype); + PROTOTYPED_FUNCTIONS.set(prototype, prototypedFunc); } - constructor(property) { - super("...a", "return this(...a);"); - if (property === undefined) { - property = CALL; - } + this[BOUND] = Function.prototype.bind.call(prototypedFunc, this); - this[BOUND_THIS] = Function.prototype.bind.call( - this, - callableFunction.bind(this) - ); + PROPERTIES.set(this, property); + PROPERTIES.set(this[BOUND], property); - Object.defineProperties(this[BOUND_THIS], { - [PROPERTY]: { - ...privateValueDescriptor, - value: property, - }, - name: Object.getOwnPropertyDescriptor(this.constructor, "name"), - }); + Object.defineProperty( + this[BOUND], + "name", + Object.getOwnPropertyDescriptor(this.constructor, "name") + ); - return this[BOUND_THIS]; + return this[BOUND]; +} + +CallableConstructor.prototype = Function.prototype; + +class Callable extends CallableConstructor { + constructor(property) { + super(property); + } + + static get CALL() { + return CALL; } static makeCallable(obj, property) { @@ -47,7 +55,6 @@ class Callable extends Function { if (prototype === Object.prototype || prototype === Callable.prototype) { const callableObject = new Callable(property); const properties = Object.getOwnPropertyDescriptors(obj); - delete properties[PROPERTY]; Object.defineProperties(callableObject, properties); return callableObject; } @@ -63,10 +70,10 @@ class Callable extends Function { obj !== null && Object.getPrototypeOf(obj) === Callable.prototype ) { - return Callable.makeCallable(obj, obj[PROPERTY]); + return Callable.makeCallable(obj, PROPERTIES.get(obj)); } throw new TypeError( - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ); } @@ -75,27 +82,15 @@ class Callable extends Function { } apply(...args) { - return this[this[PROPERTY]].apply(...args); + return this[PROPERTIES.get(this)].apply(...args); } call(...args) { - return this[this[PROPERTY]].call(...args); + return this[PROPERTIES.get(this)].call(...args); } bind(...args) { - const boundCall = this[this[PROPERTY]].bind(...args); - const bound = Function.prototype.bind.call(this, boundCall); - Object.defineProperties(bound, { - [BOUND_CALL]: { - ...privateValueDescriptor, - value: boundCall, - }, - [PROPERTY]: { - ...privateValueDescriptor, - value: BOUND_CALL, - }, - }); - return bound; + return this[PROPERTIES.get(this)].bind(...args); } } diff --git a/test/classGeneric.ts b/test/classGeneric.ts index 91af8dc..f3789e4 100644 --- a/test/classGeneric.ts +++ b/test/classGeneric.ts @@ -43,47 +43,6 @@ describe("Callable With Class Generic and custom property (TypeScript)", functio }); }); -// class RepeaterWithTSClassOverride extends Callable { -// constructor(public count: number) { -// super('go'); -// } -// go(arg: string): string -// go(arg: number): number -// go(arg: string | number): string | number { -// return arg -// // return arg.repeat(this.count); -// } -// } - -//// override is not supported in class generic - -// describe("Callable With TS Class Overload Generic and custom property (TypeScript)", function () { -// it("is callable", function () { -// expectType<{ (x: string): string, (x: number): number }>(new RepeaterWithTSClassOverride(1)); -// // @ts-expect-error wrong type for constructor -// new RepeaterWithTSClassOverride("testing"); -// new RepeaterWithTSClassOverride(5).go(5); -// // Valid propert access. -// new RepeaterWithTSClassOverride(5).count = 4; -// }); - -// it("is an object", function () { -// expectType(new RepeaterWithTSClassOverride(5)); -// expectType<(x: string) => string>(new RepeaterWithTSClassOverride(5).go); -// expectType<{ (x: string): string, (x: number): number }>(new RepeaterWithTSClassOverride(5)) -// expectType<(x: string) => string>(() => new RepeaterWithTSClassOverride(5)('23')) -// expectType<(x: number) => number>(() => new RepeaterWithTSClassOverride(5)(23)) - -// }); - -// it("is an instance of Repeater", function () { -// expectType(new RepeaterWithTSClassOverride(5)); -// expectType>(new RepeaterWithTSClassOverride(5)); -// expectType(new RepeaterWithTSClassOverride(5)); -// expectType(new RepeaterWithTSClassOverride(5)); -// }); -// }); - class RepeaterWithClassOverride extends (RepeaterWithClassGeneric as OverrideCall< typeof RepeaterWithClassGeneric >) { @@ -115,7 +74,6 @@ describe("Callable With Class Override Generic and custom property (TypeScript)" it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class - // expectType(new RepeaterWithClassOverride()); expectType(new RepeaterWithClassOverride()); expectType>( new RepeaterWithClassOverride() diff --git a/test/clone.cjs b/test/clone.cjs index 343bd1e..f9475b3 100644 --- a/test/clone.cjs +++ b/test/clone.cjs @@ -114,7 +114,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -130,7 +130,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -146,7 +146,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -162,7 +162,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -178,7 +178,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -194,7 +194,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -210,7 +210,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -226,7 +226,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } diff --git a/test/clone.mjs b/test/clone.mjs index fafe398..5f98ce0 100644 --- a/test/clone.mjs +++ b/test/clone.mjs @@ -114,7 +114,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -130,7 +130,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -146,7 +146,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -162,7 +162,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -178,7 +178,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -194,7 +194,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -210,7 +210,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } @@ -226,7 +226,7 @@ describe( } catch (e) { if ( e.message === - "Callable.clone accepts only direct instance of Callable. e.g created by Callable.makeCallable or with new Callable()" + "Callable.clone accepts only direct instance of Callable" ) { return true; } diff --git a/test/funcGeneric.ts b/test/funcGeneric.ts index 547998f..ac2e30a 100644 --- a/test/funcGeneric.ts +++ b/test/funcGeneric.ts @@ -122,7 +122,6 @@ describe("Callable With TS Func Override Generic and custom property (TypeScript it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class - // expectType(new RepeaterWithOverridenFunc()); expectType(new RepeaterWithFuncOverride()); expectType>( new RepeaterWithFuncOverride() @@ -170,7 +169,6 @@ describe("Callable With Generic Function Generic and custom property (TypeScript it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class - // expectType(new RepeaterWithOverridenFunc()); expectType(new RepeaterWithGenericFunc(23)); expectType>( new RepeaterWithGenericFunc(23) @@ -178,4 +176,4 @@ describe("Callable With Generic Function Generic and custom property (TypeScript expectType(new RepeaterWithGenericFunc(23)); expectType(new RepeaterWithGenericFunc(23)); }); -}); \ No newline at end of file +}); diff --git a/test/interfaceGeneric.ts b/test/interfaceGeneric.ts index 8b2d0cd..ea13425 100644 --- a/test/interfaceGeneric.ts +++ b/test/interfaceGeneric.ts @@ -66,7 +66,6 @@ class RepeaterWithInterfaceOverload go(arg: string): string; go(arg: string | number): string | number { return arg; - // return arg.repeat(this.count); } } @@ -208,7 +207,6 @@ describe("Callable With Generic Interface Generic and custom property (TypeScrip it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class - // expectType(new RepeaterWithInterfaceOverride()); expectType( new RepeaterWithGenericInterface() ); diff --git a/test/newCallable.cjs b/test/newCallable.cjs index ca29d28..cf88a83 100644 --- a/test/newCallable.cjs +++ b/test/newCallable.cjs @@ -25,18 +25,15 @@ function defaultTest(Class, prototypes = []) { assert(new Class("msg") instanceof defaultPrototypes[i]); } for (let i = 0; i !== prototypes.length; i++) { - assert(new Class("msg") instanceof defaultPrototypes[i]); + assert(new Class("msg") instanceof prototypes[i]); } }); - it("it's bind method correctly inherits prototypes", function () { + it("it's bind method has only Function prototype(returns instance of Function, not Callable)", function () { assert(typeof new Class("msg").bind({}) === "function"); - const defaultPrototypes = [Object, Function, Callable, Class]; + const defaultPrototypes = [Object, Function]; for (let i = 0; i !== defaultPrototypes.length; i++) { assert(new Class("msg").bind({}) instanceof defaultPrototypes[i]); } - for (let i = 0; i !== prototypes.length; i++) { - assert(new Class("msg").bind({}) instanceof defaultPrototypes[i]); - } }); it("it's call, apply method is overriden and works exactly like [CALL].apply, [CALL].call", function () { assert(typeof new Class("msg").call === "function"); @@ -46,10 +43,6 @@ function defaultTest(Class, prototypes = []) { assert(new Class("msg").call instanceof defaultPrototypes[i]); assert(new Class("msg").apply instanceof defaultPrototypes[i]); } - for (let i = 0; i !== prototypes.length; i++) { - assert(new Class("msg").call instanceof defaultPrototypes[i]); - assert(new Class("msg").apply instanceof defaultPrototypes[i]); - } }); it("copies name property from constructor", function () { assert(new Class("test").name === Class.name); diff --git a/test/newCallable.mjs b/test/newCallable.mjs index 5e213fa..3ff952e 100644 --- a/test/newCallable.mjs +++ b/test/newCallable.mjs @@ -25,18 +25,15 @@ function defaultTest(Class, prototypes = []) { assert(new Class("msg") instanceof defaultPrototypes[i]); } for (let i = 0; i !== prototypes.length; i++) { - assert(new Class("msg") instanceof defaultPrototypes[i]); + assert(new Class("msg") instanceof prototypes[i]); } }); - it("it's bind method correctly inherits prototypes", function () { + it("it's bind method has only Function prototype(returns instance of Function, not Callable)", function () { assert(typeof new Class("msg").bind({}) === "function"); - const defaultPrototypes = [Object, Function, Callable, Class]; + const defaultPrototypes = [Object, Function]; for (let i = 0; i !== defaultPrototypes.length; i++) { assert(new Class("msg").bind({}) instanceof defaultPrototypes[i]); } - for (let i = 0; i !== prototypes.length; i++) { - assert(new Class("msg").bind({}) instanceof defaultPrototypes[i]); - } }); it("it's call, apply method is overriden and works exactly like [CALL].apply, [CALL].call", function () { assert(typeof new Class("msg").call === "function"); @@ -46,10 +43,6 @@ function defaultTest(Class, prototypes = []) { assert(new Class("msg").call instanceof defaultPrototypes[i]); assert(new Class("msg").apply instanceof defaultPrototypes[i]); } - for (let i = 0; i !== prototypes.length; i++) { - assert(new Class("msg").call instanceof defaultPrototypes[i]); - assert(new Class("msg").apply instanceof defaultPrototypes[i]); - } }); it("copies name property from constructor", function () { assert(new Class("test").name === Class.name); diff --git a/test/overrideCall.ts b/test/overrideCall.ts index b8f9d99..ef8c09b 100644 --- a/test/overrideCall.ts +++ b/test/overrideCall.ts @@ -1,7 +1,5 @@ import { expectType } from "ts-expect"; -import Callable, { - OverrideCall, -} from "callable-instance"; +import Callable, { OverrideCall } from "callable-instance"; class MyClass extends Callable { constructor() { From d4deb25ea7d5992f360f627cc6e13538b5dc5119 Mon Sep 17 00:00:00 2001 From: pabadm Date: Sun, 3 Dec 2023 20:59:37 +0100 Subject: [PATCH 44/60] properties map optimisation --- lib/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index 1a757f3..16ab2c9 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,7 +7,7 @@ const PROPERTIES = new WeakMap(); function createFunc() { return function callableFunction(...args) { - return this[BOUND][PROPERTIES.get(this)](...args); + return this[BOUND][PROPERTIES.get(this[BOUND])](...args); }; } @@ -26,7 +26,6 @@ function CallableConstructor(property) { this[BOUND] = Function.prototype.bind.call(prototypedFunc, this); - PROPERTIES.set(this, property); PROPERTIES.set(this[BOUND], property); Object.defineProperty( From c4c16249d34e88b30e064a6603cf8b12d95f0ea3 Mon Sep 17 00:00:00 2001 From: pabadm Date: Wed, 6 Dec 2023 03:57:20 +0100 Subject: [PATCH 45/60] code simplification (this.constructor => new.target) --- lib/index.js | 56 +++++++++++++++++++++------------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/lib/index.js b/lib/index.js index 16ab2c9..f03bd02 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,47 +1,37 @@ "use strict"; const CALL = Symbol("Callable.call"); -const BOUND = Symbol("Callable.bound"); const PROTOTYPED_FUNCTIONS = new WeakMap(); const PROPERTIES = new WeakMap(); -function createFunc() { - return function callableFunction(...args) { - return this[BOUND][PROPERTIES.get(this[BOUND])](...args); - }; -} - -function CallableConstructor(property) { - if (property === undefined) { - property = CALL; - } - - const prototype = this.constructor.prototype; - - let prototypedFunc = PROTOTYPED_FUNCTIONS.get(prototype); - if (prototypedFunc === undefined) { - prototypedFunc = Object.setPrototypeOf(createFunc(), prototype); - PROTOTYPED_FUNCTIONS.set(prototype, prototypedFunc); - } - - this[BOUND] = Function.prototype.bind.call(prototypedFunc, this); +class Callable extends Function { + constructor(property) { + if (property === undefined) { + property = CALL; + } - PROPERTIES.set(this[BOUND], property); + const prototype = new.target.prototype; + let prototypedFunc = PROTOTYPED_FUNCTIONS.get(prototype); + if (prototypedFunc === undefined) { + function callableFunction(...args) { + return this.a[PROPERTIES.get(this.a)](...args); + } + prototypedFunc = Object.setPrototypeOf(callableFunction, prototype); + PROTOTYPED_FUNCTIONS.set(prototype, prototypedFunc); + } - Object.defineProperty( - this[BOUND], - "name", - Object.getOwnPropertyDescriptor(this.constructor, "name") - ); + const funcThis = {}; + funcThis.a = Function.prototype.bind.call(prototypedFunc, funcThis); - return this[BOUND]; -} + PROPERTIES.set(funcThis.a, property); -CallableConstructor.prototype = Function.prototype; + Object.defineProperty( + funcThis.a, + "name", + Object.getOwnPropertyDescriptor(new.target, "name") + ); -class Callable extends CallableConstructor { - constructor(property) { - super(property); + return funcThis.a; } static get CALL() { From b67c21fc4a86a3870245405595669b3eb657e918 Mon Sep 17 00:00:00 2001 From: pabadm Date: Wed, 6 Dec 2023 04:31:19 +0100 Subject: [PATCH 46/60] edited package.json 'files' --- package.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 1599c51..81b32eb 100644 --- a/package.json +++ b/package.json @@ -38,9 +38,7 @@ "typescript": "^4.9.4" }, "files": [ - "index.js", - "index.d.ts", - "LICENSE", - "README.md" + "lib/index.js", + "lib/index.d.ts" ] } From fe245efc8cd3ecad4471414cc8bbfd933c974665 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:03:34 +0100 Subject: [PATCH 47/60] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ea56060..0563238 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ const cloned2 = Callable.clone(callableObject); // 2nd optional generic is for method name (string | symbol | number) which will be used as type of call signature from 1st generic (defaults to Callable.CALL) interface IExampleClass { - // interface type will provide actual type of the function without limits + // interface type will provide actual type of the function without limitations [Callable.CALL](arg: string): string } // implements is optional but advised https://www.typescriptlang.org/docs/handbook/interfaces.html @@ -145,7 +145,7 @@ It can be used to override call signature in child classes. ```typescript // Override call has 3 generics but must be written only in one way // class Child extends (Parent as OverrideCall) -// 1st generic is Parent +// 1st generic is Parent. Can be only class. // 2nd generic is Child. Can be interface | class | function // 3rd optional generic is propertyName can be string | symbol | number. defaults to Callable.CALL From 30b3a3ea9b81bba42218a5fc017299ed571fe580 Mon Sep 17 00:00:00 2001 From: pabadm Date: Wed, 6 Dec 2023 22:19:54 +0100 Subject: [PATCH 48/60] test fixes --- test/clone.cjs | 10 ---------- test/clone.mjs | 10 ---------- test/makeCallable.cjs | 6 ------ test/makeCallable.mjs | 6 ------ test/newCallable.cjs | 13 +++---------- test/newCallable.mjs | 11 ++--------- 6 files changed, 5 insertions(+), 51 deletions(-) diff --git a/test/clone.cjs b/test/clone.cjs index f9475b3..a279199 100644 --- a/test/clone.cjs +++ b/test/clone.cjs @@ -67,16 +67,6 @@ function defaultTest(clone, source, name, prototypes = []) { it("has length property set to 0 due to ...args", function () { assert(clone(source).length === 0); }); - it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { - assert(clone(source).func === undefined); - assert(clone(source).bound === undefined); - assert( - Object.getOwnPropertyDescriptor(clone(source), "func") === undefined - ); - assert( - Object.getOwnPropertyDescriptor(clone(source), "bound") === undefined - ); - }); }); } diff --git a/test/clone.mjs b/test/clone.mjs index 5f98ce0..06ed8da 100644 --- a/test/clone.mjs +++ b/test/clone.mjs @@ -67,16 +67,6 @@ function defaultTest(clone, source, name, prototypes = []) { it("has length property set to 0 due to ...args", function () { assert(clone(source).length === 0); }); - it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { - assert(clone(source).func === undefined); - assert(clone(source).bound === undefined); - assert( - Object.getOwnPropertyDescriptor(clone(source), "func") === undefined - ); - assert( - Object.getOwnPropertyDescriptor(clone(source), "bound") === undefined - ); - }); }); } diff --git a/test/makeCallable.cjs b/test/makeCallable.cjs index f1cf05f..e5459d7 100644 --- a/test/makeCallable.cjs +++ b/test/makeCallable.cjs @@ -34,12 +34,6 @@ function defaultTest(create, name, prototypes = []) { it("has length property set to 0 due to ...args", function () { assert(create().length === 0); }); - it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { - assert(create().func === undefined); - assert(create().bound === undefined); - assert(Object.getOwnPropertyDescriptor(create(), "func") === undefined); - assert(Object.getOwnPropertyDescriptor(create(), "bound") === undefined); - }); }); } diff --git a/test/makeCallable.mjs b/test/makeCallable.mjs index 501dadb..aaddc1a 100644 --- a/test/makeCallable.mjs +++ b/test/makeCallable.mjs @@ -34,12 +34,6 @@ function defaultTest(create, name, prototypes = []) { it("has length property set to 0 due to ...args", function () { assert(create().length === 0); }); - it("has not accessible properties func, property, bound (because it returns this.bound instead of this)", function () { - assert(create().func === undefined); - assert(create().bound === undefined); - assert(Object.getOwnPropertyDescriptor(create(), "func") === undefined); - assert(Object.getOwnPropertyDescriptor(create(), "bound") === undefined); - }); }); } diff --git a/test/newCallable.cjs b/test/newCallable.cjs index cf88a83..d7d73bc 100644 --- a/test/newCallable.cjs +++ b/test/newCallable.cjs @@ -5,7 +5,7 @@ function getTitle(Class, isDefault) { return `${Class.name}${isDefault ? " default" : ""} (cjs)`; } -describe(getTitle(Callable) + " C Class Test", function () { +describe(getTitle(Callable) + " Callable Class Test", function () { it("correctly inherits prototypes", function () { assert(new Callable() instanceof Object); assert(new Callable() instanceof Function); @@ -50,13 +50,6 @@ function defaultTest(Class, prototypes = []) { it("has length property set to 0 due to ...args", function () { assert(new Class("testing").length === 0); }); - it("has not accessible properties bound (because it returns this.bound instead of this)", function () { - assert(new Class("testing").bound === undefined); - assert( - Object.getOwnPropertyDescriptor(new Class("testing"), "bound") === - undefined - ); - }); }); } @@ -85,7 +78,7 @@ describe(getTitle(MyTest), function () { assert(test() === "new message"); assert(test.go() === "new message"); }); - it("has own string tag C", function () { + it("has own string tag Callable", function () { assert( Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" ); @@ -130,7 +123,7 @@ describe(getTitle(MyTestExtended), function () { assert(test() === "new message"); assert(test.go() === "new message"); }); - it("has own string tag C", function () { + it("has own string tag Callable", function () { assert( Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" ); diff --git a/test/newCallable.mjs b/test/newCallable.mjs index 3ff952e..13e3773 100644 --- a/test/newCallable.mjs +++ b/test/newCallable.mjs @@ -50,13 +50,6 @@ function defaultTest(Class, prototypes = []) { it("has length property set to 0 due to ...args", function () { assert(new Class("testing").length === 0); }); - it("has not accessible properties bound (because it returns this.bound instead of this)", function () { - assert(new Class("testing").bound === undefined); - assert( - Object.getOwnPropertyDescriptor(new Class("testing"), "bound") === - undefined - ); - }); }); } @@ -85,7 +78,7 @@ describe(getTitle(MyTest), function () { assert(test() === "new message"); assert(test.go() === "new message"); }); - it("has own string tag C", function () { + it("has own string tag Callable", function () { assert( Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" ); @@ -130,7 +123,7 @@ describe(getTitle(MyTestExtended), function () { assert(test() === "new message"); assert(test.go() === "new message"); }); - it("has own string tag C", function () { + it("has own string tag Callable", function () { assert( Object.prototype.toString.call(new MyTest("test")) === "[object Callable]" ); From 8b471612e5a2fa9392e148fbd27aa32a6c2d0c50 Mon Sep 17 00:00:00 2001 From: pabadm Date: Thu, 7 Dec 2023 02:49:51 +0100 Subject: [PATCH 49/60] inlined some code --- lib/index.js | 23 ++++++++++++----------- package.json | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/index.js b/lib/index.js index f03bd02..1801393 100644 --- a/lib/index.js +++ b/lib/index.js @@ -10,14 +10,12 @@ class Callable extends Function { property = CALL; } - const prototype = new.target.prototype; - let prototypedFunc = PROTOTYPED_FUNCTIONS.get(prototype); + let prototypedFunc = PROTOTYPED_FUNCTIONS.get(new.target.prototype); if (prototypedFunc === undefined) { - function callableFunction(...args) { + prototypedFunc = Object.setPrototypeOf(function callable(...args) { return this.a[PROPERTIES.get(this.a)](...args); - } - prototypedFunc = Object.setPrototypeOf(callableFunction, prototype); - PROTOTYPED_FUNCTIONS.set(prototype, prototypedFunc); + }, new.target.prototype); + PROTOTYPED_FUNCTIONS.set(new.target.prototype, prototypedFunc); } const funcThis = {}; @@ -42,10 +40,10 @@ class Callable extends Function { if (obj !== undefined && obj !== null) { const prototype = Object.getPrototypeOf(obj); if (prototype === Object.prototype || prototype === Callable.prototype) { - const callableObject = new Callable(property); - const properties = Object.getOwnPropertyDescriptors(obj); - Object.defineProperties(callableObject, properties); - return callableObject; + return Object.defineProperties( + new Callable(property), + Object.getOwnPropertyDescriptors(obj) + ); } } throw new TypeError( @@ -59,7 +57,10 @@ class Callable extends Function { obj !== null && Object.getPrototypeOf(obj) === Callable.prototype ) { - return Callable.makeCallable(obj, PROPERTIES.get(obj)); + return Object.defineProperties( + new Callable(PROPERTIES.get(obj)), + Object.getOwnPropertyDescriptors(obj) + ); } throw new TypeError( "Callable.clone accepts only direct instance of Callable" diff --git a/package.json b/package.json index 81b32eb..125cadb 100644 --- a/package.json +++ b/package.json @@ -41,4 +41,4 @@ "lib/index.js", "lib/index.d.ts" ] -} +} \ No newline at end of file From c2bb931578082d5751a9e26fad6c3faf2f5cd6ad Mon Sep 17 00:00:00 2001 From: pabadm Date: Thu, 7 Dec 2023 07:32:40 +0100 Subject: [PATCH 50/60] TS: better function type detection --- lib/index.d.ts | 17 +++++++++++- test/interfaceGeneric.ts | 57 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index 1caca0c..cdf4d5a 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -15,19 +15,34 @@ declare module "callable-instance" { P extends BaseProperty > = I[P] extends BaseFunc ? I[P] : never; + /** + * call signature in interface type e.g {(...args: any): any} is considered function type + * call signature in interface type combined with other properties e.g {(...args: any): any, property: any} is considered interface type. + */ + type ExtractFuncFromFuncType< + F extends BaseFunc, + P extends BaseProperty + > = Omit extends Record< + BaseProperty, + never + > + ? F + : ExtractFuncFromInterface; + interface CloneFuncFromClass { /** * For TS generics and function overload support use interface or function type for Callable */ (...args: Parameters[P]>): ReturnType[P]>; } + type ExtractFunc< C extends BaseClass | BaseFunc | BaseInterface, P extends BaseProperty > = C extends BaseClass ? CloneFuncFromClass : C extends BaseFunc - ? C + ? ExtractFuncFromFuncType : C extends BaseInterface ? ExtractFuncFromInterface : never; diff --git a/test/interfaceGeneric.ts b/test/interfaceGeneric.ts index ea13425..6fdfac8 100644 --- a/test/interfaceGeneric.ts +++ b/test/interfaceGeneric.ts @@ -217,3 +217,60 @@ describe("Callable With Generic Interface Generic and custom property (TypeScrip expectType(new RepeaterWithGenericInterface()); }); }); + +interface IFuncInterface { + (arg: "notCallable"): "test"; + go(g: G): G; +} + +class RepeaterWithFuncInterface + extends Callable +{ + constructor() { + super("go"); + } + public count = 23; + go(g) { + return g; + } +} + +describe("Callable With Func Interface Generic", function () { + it("is callable", function () { + expectType<(arg: G) => G>( + new RepeaterWithFuncInterface() + ); + new RepeaterWithFuncInterface()("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithFuncInterface()(5).go(5); + // Valid propert access. + new RepeaterWithFuncInterface().count = 4; + }); + + it("should not use call signature of interface as type", function () { + expectType<(arg: "notCallable") => "test">( + // @ts-expect-error wrong type + new RepeaterWithFuncInterface() + ); + new RepeaterWithFuncInterface()("testing"); + // @ts-expect-error wrong type for method + new RepeaterWithFuncInterface()(5).go(5); + // Valid propert access. + new RepeaterWithFuncInterface().count = 4; + }); + + it("is an object", function () { + expectType( + new RepeaterWithFuncInterface() + ); + expectType<(arg: G) => G>( + new RepeaterWithFuncInterface().go + ); + expectType<(arg: G) => G>( + new RepeaterWithFuncInterface() + ); + expectType<(arg: G) => G>( + new RepeaterWithFuncInterface().go + ); + }); +}); From b7639deb86be5345a863a075ea4a806a50de3806 Mon Sep 17 00:00:00 2001 From: pabadm Date: Thu, 7 Dec 2023 20:01:51 +0100 Subject: [PATCH 51/60] inlined more --- lib/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/index.js b/lib/index.js index 1801393..ab56c23 100644 --- a/lib/index.js +++ b/lib/index.js @@ -23,13 +23,11 @@ class Callable extends Function { PROPERTIES.set(funcThis.a, property); - Object.defineProperty( + return Object.defineProperty( funcThis.a, "name", Object.getOwnPropertyDescriptor(new.target, "name") ); - - return funcThis.a; } static get CALL() { From bada7a992fd64e87c6e275e7c306784146d88c16 Mon Sep 17 00:00:00 2001 From: pabadm Date: Sat, 16 Dec 2023 19:57:15 +0100 Subject: [PATCH 52/60] sideEffects: false --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 125cadb..220cb91 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "callable-instance", + "sideEffects": false, "version": "3.0.0", "description": "Instances of classes which are directly callable as functions.", "repository": "CGamesPlay/node-callable-instance", From eb09e33b97002d8521eda845929077f4550a96c1 Mon Sep 17 00:00:00 2001 From: pabadm <47691385+pabadm@users.noreply.github.com> Date: Sat, 3 Feb 2024 07:23:58 +0100 Subject: [PATCH 53/60] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f87b7bc..1325b51 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,10 @@ In the next example, we will create `callableObject` using Callable.from. import Callable from "callable-instance"; // Callable.from creates new callable object with all the properties from source object const callableObject = Callable.from( { - test: "test", - [ Callable.CALL ]() { - return this.test; - }, + test: "test", + [ Callable.CALL ]() { + return this.test; + }, } ); // cloning using spread operator + Callable.from. (spread looses call signature so it is important to call Callable.from again) @@ -73,7 +73,7 @@ const cloned2 = Callable.clone( callableObject ); import Callable from "callable-instance"; const callableObject = Callable.from( { - test: "test", + test: "test", getTest() { return this.test; }, From 4741517e6f88079e1cc6a4d37465ec0084f44fcf Mon Sep 17 00:00:00 2001 From: pabadm Date: Sat, 3 Feb 2024 10:22:47 +0100 Subject: [PATCH 54/60] Callable.makeCallable => Callable.from + improved typings --- .idea/.gitignore | 8 + .idea/inspectionProfiles/Project_Default.xml | 191 +++++++++++++++++++ .idea/modules.xml | 8 + .idea/node-callable-instance-old.iml | 8 + .idea/php.xml | 19 ++ .idea/vcs.xml | 6 + README.md | 48 ++--- lib/index.d.ts | 142 +++++++------- lib/index.js | 4 +- test/classGeneric.ts | 10 +- test/clone.cjs | 2 +- test/clone.mjs | 2 +- test/funcGeneric.ts | 18 +- test/interfaceGeneric.ts | 18 +- test/makeCallable.cjs | 39 ++-- test/makeCallable.mjs | 39 ++-- tsconfig.json | 6 + 17 files changed, 398 insertions(+), 170 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/node-callable-instance-old.iml create mode 100644 .idea/php.xml create mode 100644 .idea/vcs.xml create mode 100644 tsconfig.json diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..152f85c --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,191 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b8507fc --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/node-callable-instance-old.iml b/.idea/node-callable-instance-old.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/node-callable-instance-old.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..f324872 --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 0563238..4d0322e 100644 --- a/README.md +++ b/README.md @@ -49,39 +49,41 @@ class ExampleClassWithCustomMethodName extends Callable { ``` ### Other Usage Variant -In the next example, we will create `callableObject` using Callable.makeCallable. +In the next example, we will create `callableObject` using Callable.from. ```javascript import Callable from "callable-instance"; -// makeCallable creates new callable object with all the properties from source object -const callableObject = Callable.makeCallable({ - test: "test", - [Callable.CALL]() { - return this.test; - }, -}); - -// cloning using spread operator + makeCallable. (spread looses call signature so it is important to call makeCallable again) -const cloned = Callable.makeCallable({...callableObject}); - -// cloning using Callable.clone (accepts only direct instance of Callable. e.g. made with makeCallable) -const cloned2 = Callable.clone(callableObject); +// Callable.from creates new callable object with all the properties from source object +const callableObject = Callable.from( { + test: "test", + [ Callable.CALL ]() { + return this.test; + }, +} ); + +// cloning using spread operator + Callable.from. (spread looses call signature so it is important to call Callable.from again) +const cloned = Callable.from( { ...callableObject } ); + +// cloning using Callable.clone (accepts only direct instance of Callable. e.g. made with Callable.from) +const cloned2 = Callable.clone( callableObject ); ``` > **_NOTE:_** Usage of custom method name is also supported. + ```javascript import Callable from "callable-instance"; -const callableObject = Callable.makeCallable({ - test: "test", - getTest() { - return this.test; - }, -}, "getTest"); // second parameter is optional method`s name which will be used when calling object -// but it is important to also provide method name when cloning using spread + makeCallable -const cloned = Callable.makeCallable({...callableObject}, "getTest"); +const callableObject = Callable.from( { + test: "test", + getTest() { + return this.test; + }, +}, "getTest" ); // second parameter is optional method`s name which will be used when calling object + +// but it is important to also provide method property when cloning using spread + Callable.from +const cloned = Callable.from( { ...callableObject }, "getTest" ); // Callable.clone does not have this issue -const cloned2 = Callable.clone(callableObject); +const cloned2 = Callable.clone( callableObject ); ``` ## Typescript diff --git a/lib/index.d.ts b/lib/index.d.ts index cdf4d5a..4b71ae4 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,82 +1,84 @@ -declare module "callable-instance" { - const CALL: unique symbol; +declare const CALL: unique symbol; - type BaseProperty = symbol | string | number; - type CustomProperty = Exclude; +type BaseProperty = symbol | string | number; +type CustomProperty = Exclude; - type BaseFunc = (...args: any) => any; - type BaseClass = abstract new (...args: any) => any; - type BaseInterface = { - [k: BaseProperty]: any; - }; +type BaseFunc = (...args: any) => any; +type BaseClass = abstract new (...args: any) => any; +type BaseInterface = { + [k: BaseProperty]: any; +}; - type ExtractFuncFromInterface< - I extends BaseInterface, - P extends BaseProperty - > = I[P] extends BaseFunc ? I[P] : never; +type ExtractFuncFromInterface< + I extends BaseInterface, + P extends BaseProperty +> = I[P] extends BaseFunc ? I[P] : never; +/** + * call signature in interface type e.g {(...args: any): any} is considered function type + * call signature in interface type combined with other properties e.g {(...args: any): any, property: any} is considered interface type. + */ +type ExtractFuncFromFuncType = Omit< + F, + never +> extends Record + ? F + : ExtractFuncFromInterface; + +interface CloneFuncFromClass { /** - * call signature in interface type e.g {(...args: any): any} is considered function type - * call signature in interface type combined with other properties e.g {(...args: any): any, property: any} is considered interface type. + * For TS generics and function overload support use interface or function type for Callable */ - type ExtractFuncFromFuncType< - F extends BaseFunc, - P extends BaseProperty - > = Omit extends Record< - BaseProperty, - never - > - ? F - : ExtractFuncFromInterface; - - interface CloneFuncFromClass { - /** - * For TS generics and function overload support use interface or function type for Callable - */ - (...args: Parameters[P]>): ReturnType[P]>; - } - - type ExtractFunc< + (...args: Parameters[P]>): ReturnType[P]>; +} + +type ExtractFunc< + C extends BaseClass | BaseFunc | BaseInterface, + P extends BaseProperty +> = C extends BaseClass + ? CloneFuncFromClass + : C extends BaseFunc + ? ExtractFuncFromFuncType + : C extends BaseInterface + ? ExtractFuncFromInterface + : never; + +type PickProperties> = { + [k in keyof Obj]: Obj[k]; +}; + +interface CallableConstructor { + get CALL(): typeof CALL; + + from( + callableLike: I, + property: P + ): PickProperties & ExtractFuncFromInterface; + + from( + callableLike: I + ): PickProperties & ExtractFuncFromInterface; + + clone(callable: C): C; + + new < C extends BaseClass | BaseFunc | BaseInterface, - P extends BaseProperty - > = C extends BaseClass - ? CloneFuncFromClass - : C extends BaseFunc - ? ExtractFuncFromFuncType - : C extends BaseInterface - ? ExtractFuncFromInterface - : never; - - export interface CallableConstructor { - get CALL(): typeof CALL; - - makeCallable( - object: I, - property: P - ): PickProperties & ExtractFuncFromInterface; - makeCallable( - object: I - ): PickProperties & ExtractFuncFromInterface; - - clone(callableObject: C): C; + P extends CustomProperty + >( + property: P + ): ExtractFunc; - new < - C extends BaseClass | BaseFunc | BaseInterface, - P extends CustomProperty - >( - property: P - ): ExtractFunc; + new (): ExtractFunc< + C, + typeof CALL + >; +} - new (): ExtractFunc< - C, - typeof CALL - >; - } +export = Callable; - type PickProperties> = { - [k in keyof Obj]: Obj[k]; - }; +declare const Callable: CallableConstructor; +declare namespace Callable { export type OverrideCall = { new < C extends BaseClass | BaseFunc | BaseInterface, @@ -85,8 +87,4 @@ declare module "callable-instance" { ...args: ConstructorParameters ): Omit>, P> & ExtractFunc; } & S; - - const Callable: CallableConstructor; - - export default Callable; } diff --git a/lib/index.js b/lib/index.js index ab56c23..25b2ad2 100644 --- a/lib/index.js +++ b/lib/index.js @@ -34,7 +34,7 @@ class Callable extends Function { return CALL; } - static makeCallable(obj, property) { + static from(obj, property) { if (obj !== undefined && obj !== null) { const prototype = Object.getPrototypeOf(obj); if (prototype === Object.prototype || prototype === Callable.prototype) { @@ -45,7 +45,7 @@ class Callable extends Function { } } throw new TypeError( - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ); } diff --git a/test/classGeneric.ts b/test/classGeneric.ts index f3789e4..d8ac2a6 100644 --- a/test/classGeneric.ts +++ b/test/classGeneric.ts @@ -1,5 +1,5 @@ import { expectType } from "ts-expect"; -import Callable, { CallableConstructor, OverrideCall } from "callable-instance"; +import Callable, { OverrideCall } from "callable-instance"; // TESTS FOR CLASS-TYPE GENERICS @@ -35,9 +35,7 @@ describe("Callable With Class Generic and custom property (TypeScript)", functio it("is an instance of Repeater", function () { expectType(new RepeaterWithClassGeneric(5)); - expectType>( - new RepeaterWithClassGeneric(5) - ); + expectType>(new RepeaterWithClassGeneric(5)); expectType(new RepeaterWithClassGeneric(5)); expectType(new RepeaterWithClassGeneric(5)); }); @@ -75,9 +73,7 @@ describe("Callable With Class Override Generic and custom property (TypeScript)" it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class expectType(new RepeaterWithClassOverride()); - expectType>( - new RepeaterWithClassOverride() - ); + expectType>(new RepeaterWithClassOverride()); expectType(new RepeaterWithClassOverride()); expectType(new RepeaterWithClassOverride()); }); diff --git a/test/clone.cjs b/test/clone.cjs index a279199..963c5f6 100644 --- a/test/clone.cjs +++ b/test/clone.cjs @@ -74,7 +74,7 @@ defaultTest(cloneCallable, new Callable("test"), "clone of regular callable"); defaultTest( cloneCallable, - Callable.makeCallable({ + Callable.from({ test: "test", [Callable.CALL]() { return this.test; diff --git a/test/clone.mjs b/test/clone.mjs index 06ed8da..5876e6d 100644 --- a/test/clone.mjs +++ b/test/clone.mjs @@ -74,7 +74,7 @@ defaultTest(cloneCallable, new Callable("test"), "clone of regular callable"); defaultTest( cloneCallable, - Callable.makeCallable({ + Callable.from({ test: "test", [Callable.CALL]() { return this.test; diff --git a/test/funcGeneric.ts b/test/funcGeneric.ts index ac2e30a..25ef9b7 100644 --- a/test/funcGeneric.ts +++ b/test/funcGeneric.ts @@ -1,5 +1,5 @@ import { expectType } from "ts-expect"; -import Callable, { CallableConstructor, OverrideCall } from "callable-instance"; +import Callable, { OverrideCall } from "callable-instance"; // TESTS FOR FUNCTION-TYPE GENERICS class RepeaterWithFuncGeneric extends Callable<(x: string) => string, "go"> { @@ -31,9 +31,7 @@ describe("Callable With Func Generic and custom property (TypeScript)", function it("is an instance of Repeater", function () { expectType(new RepeaterWithFuncGeneric(5)); - expectType>( - new RepeaterWithFuncGeneric(5) - ); + expectType>(new RepeaterWithFuncGeneric(5)); expectType(new RepeaterWithFuncGeneric(5)); expectType(new RepeaterWithFuncGeneric(5)); }); @@ -83,9 +81,7 @@ describe("Callable With Func overload Generic and custom property (TypeScript)", it("is an instance of Repeater", function () { expectType(new RepeaterWithFuncOverload(5)); - expectType>( - new RepeaterWithFuncOverload(5) - ); + expectType>(new RepeaterWithFuncOverload(5)); expectType(new RepeaterWithFuncOverload(5)); expectType(new RepeaterWithFuncOverload(5)); }); @@ -123,9 +119,7 @@ describe("Callable With TS Func Override Generic and custom property (TypeScript it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class expectType(new RepeaterWithFuncOverride()); - expectType>( - new RepeaterWithFuncOverride() - ); + expectType>(new RepeaterWithFuncOverride()); expectType(new RepeaterWithFuncOverride()); expectType(new RepeaterWithFuncOverride()); }); @@ -170,9 +164,7 @@ describe("Callable With Generic Function Generic and custom property (TypeScript it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class expectType(new RepeaterWithGenericFunc(23)); - expectType>( - new RepeaterWithGenericFunc(23) - ); + expectType>(new RepeaterWithGenericFunc(23)); expectType(new RepeaterWithGenericFunc(23)); expectType(new RepeaterWithGenericFunc(23)); }); diff --git a/test/interfaceGeneric.ts b/test/interfaceGeneric.ts index 6fdfac8..0326a90 100644 --- a/test/interfaceGeneric.ts +++ b/test/interfaceGeneric.ts @@ -1,5 +1,5 @@ import { expectType } from "ts-expect"; -import Callable, { CallableConstructor, OverrideCall } from "callable-instance"; +import Callable, { OverrideCall } from "callable-instance"; // TESTS FOR INTERFACE-TYPE GENERICS interface IRepeaterWithInterfaceGeneric { @@ -42,7 +42,7 @@ describe("Callable With Interface Generic and custom property (TypeScript)", fun expectType( new RepeaterWithInterfaceGeneric(5) ); - expectType>( + expectType>( new RepeaterWithInterfaceGeneric(5) ); expectType(new RepeaterWithInterfaceGeneric(5)); @@ -101,7 +101,7 @@ describe("Callable With TS Interface Overload Generic and custom property (TypeS expectType( new RepeaterWithInterfaceOverload(5) ); - expectType>( + expectType>( new RepeaterWithInterfaceOverload(5) ); expectType(new RepeaterWithInterfaceOverload(5)); @@ -153,7 +153,7 @@ describe("Callable With Interface override Generic and custom property (TypeScri expectType( new RepeaterWithInterfaceOverride() ); - expectType>( + expectType>( new RepeaterWithInterfaceOverride() ); expectType(new RepeaterWithInterfaceOverride()); @@ -210,7 +210,7 @@ describe("Callable With Generic Interface Generic and custom property (TypeScrip expectType( new RepeaterWithGenericInterface() ); - expectType>( + expectType>( new RepeaterWithGenericInterface() ); expectType(new RepeaterWithGenericInterface()); @@ -223,9 +223,7 @@ interface IFuncInterface { go(g: G): G; } -class RepeaterWithFuncInterface - extends Callable -{ +class RepeaterWithFuncInterface extends Callable { constructor() { super("go"); } @@ -260,9 +258,7 @@ describe("Callable With Func Interface Generic", function () { }); it("is an object", function () { - expectType( - new RepeaterWithFuncInterface() - ); + expectType(new RepeaterWithFuncInterface()); expectType<(arg: G) => G>( new RepeaterWithFuncInterface().go ); diff --git a/test/makeCallable.cjs b/test/makeCallable.cjs index e5459d7..1291dea 100644 --- a/test/makeCallable.cjs +++ b/test/makeCallable.cjs @@ -2,11 +2,11 @@ const assert = require("assert"); const Callable = require("callable-instance"); function getTitle(name, isDefault) { - return `Callable.makeCallable() ${name}${isDefault ? " default" : ""} (cjs)`; + return `Callable.from() ${name}${isDefault ? " default" : ""} (cjs)`; } const createRegular = () => - Callable.makeCallable({ + Callable.from({ test: "test", [Callable.CALL]() { return this.test; @@ -52,8 +52,7 @@ class CallableChild extends Callable { } } -const createFromCallableChild = () => - Callable.makeCallable(new CallableChild()); +const createFromCallableChild = () => Callable.from(new CallableChild()); class ObjectChild extends Object { constructor() { @@ -64,11 +63,11 @@ class ObjectChild extends Object { } } -const createFromObjectChild = () => Callable.makeCallable(new ObjectChild()); +const createFromObjectChild = () => Callable.from(new ObjectChild()); describe( getTitle( - "makeCallable from CallableChild | Array | ObjectChild | null | undefined | Function etc", + "Callable.from with CallableChild | Array | ObjectChild | null | undefined | Function argument", true ), function () { @@ -81,7 +80,7 @@ describe( } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -97,7 +96,7 @@ describe( } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -108,12 +107,12 @@ describe( assert( (() => { try { - Callable.makeCallable(null); + Callable.from(null); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -124,12 +123,12 @@ describe( assert( (() => { try { - Callable.makeCallable(undefined); + Callable.from(undefined); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -140,12 +139,12 @@ describe( assert( (() => { try { - Callable.makeCallable([]); + Callable.from([]); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -156,12 +155,12 @@ describe( assert( (() => { try { - Callable.makeCallable(function () {}); + Callable.from(function () {}); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -172,12 +171,12 @@ describe( assert( (() => { try { - Callable.makeCallable(23); + Callable.from(23); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -188,12 +187,12 @@ describe( assert( (() => { try { - Callable.makeCallable("123"); + Callable.from("123"); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } diff --git a/test/makeCallable.mjs b/test/makeCallable.mjs index aaddc1a..483c872 100644 --- a/test/makeCallable.mjs +++ b/test/makeCallable.mjs @@ -2,11 +2,11 @@ import assert from "assert"; import Callable from "callable-instance"; function getTitle(name, isDefault) { - return `Callable.makeCallable() ${name}${isDefault ? " default" : ""} (mjs)`; + return `Callable.from() ${name}${isDefault ? " default" : ""} (mjs)`; } const createRegular = () => - Callable.makeCallable({ + Callable.from({ test: "test", [Callable.CALL]() { return this.test; @@ -52,8 +52,7 @@ class CallableChild extends Callable { } } -const createFromCallableChild = () => - Callable.makeCallable(new CallableChild()); +const createFromCallableChild = () => Callable.from(new CallableChild()); class ObjectChild extends Object { constructor() { @@ -64,11 +63,11 @@ class ObjectChild extends Object { } } -const createFromObjectChild = () => Callable.makeCallable(new ObjectChild()); +const createFromObjectChild = () => Callable.from(new ObjectChild()); describe( getTitle( - "makeCallable from CallableChild | Array | ObjectChild | null | undefined | Function etc", + "Callable.from with CallableChild | Array | ObjectChild | null | undefined | Function argument", true ), function () { @@ -81,7 +80,7 @@ describe( } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -97,7 +96,7 @@ describe( } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -108,12 +107,12 @@ describe( assert( (() => { try { - Callable.makeCallable(null); + Callable.from(null); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -124,12 +123,12 @@ describe( assert( (() => { try { - Callable.makeCallable(undefined); + Callable.from(undefined); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -140,12 +139,12 @@ describe( assert( (() => { try { - Callable.makeCallable([]); + Callable.from([]); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -156,12 +155,12 @@ describe( assert( (() => { try { - Callable.makeCallable(function () {}); + Callable.from(function () {}); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -172,12 +171,12 @@ describe( assert( (() => { try { - Callable.makeCallable(23); + Callable.from(23); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } @@ -188,12 +187,12 @@ describe( assert( (() => { try { - Callable.makeCallable("123"); + Callable.from("123"); return false; } catch (e) { if ( e.message === - "Callable.makeCallable accepts only regular object or direct instance of Callable" + "Callable.from accepts only regular object or direct instance of Callable" ) { return true; } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..fc3f52d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + } +} \ No newline at end of file From 7b63b842401b1fb91196a5c20fc7a27ca0a6f37b Mon Sep 17 00:00:00 2001 From: pabadm Date: Sat, 3 Feb 2024 10:25:42 +0100 Subject: [PATCH 55/60] deleted .idea --- .idea/.gitignore | 8 - .idea/inspectionProfiles/Project_Default.xml | 191 ------------------- .idea/modules.xml | 8 - .idea/node-callable-instance-old.iml | 8 - .idea/php.xml | 19 -- .idea/vcs.xml | 6 - 6 files changed, 240 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/node-callable-instance-old.iml delete mode 100644 .idea/php.xml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 152f85c..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,191 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index b8507fc..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/node-callable-instance-old.iml b/.idea/node-callable-instance-old.iml deleted file mode 100644 index c956989..0000000 --- a/.idea/node-callable-instance-old.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml deleted file mode 100644 index f324872..0000000 --- a/.idea/php.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 1951f382896f44a8c7d6a5de891e56c4aee2a324 Mon Sep 17 00:00:00 2001 From: pabadm Date: Sat, 3 Feb 2024 10:47:38 +0100 Subject: [PATCH 56/60] test rename --- .gitignore | 5 ++++- test/{makeCallable.cjs => from.cjs} | 0 test/{makeCallable.mjs => from.mjs} | 0 3 files changed, 4 insertions(+), 1 deletion(-) rename test/{makeCallable.cjs => from.cjs} (100%) rename test/{makeCallable.mjs => from.mjs} (100%) diff --git a/.gitignore b/.gitignore index e9a3d6e..0e600e9 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,7 @@ jspm_packages .npm # Optional REPL history -.node_repl_history \ No newline at end of file +.node_repl_history + +# .idea +.idea \ No newline at end of file diff --git a/test/makeCallable.cjs b/test/from.cjs similarity index 100% rename from test/makeCallable.cjs rename to test/from.cjs diff --git a/test/makeCallable.mjs b/test/from.mjs similarity index 100% rename from test/makeCallable.mjs rename to test/from.mjs From 1599dcd3f7edac30e824fe9c57abb4285140e91a Mon Sep 17 00:00:00 2001 From: pabadm Date: Sat, 3 Feb 2024 10:53:49 +0100 Subject: [PATCH 57/60] readme code formatting --- README.md | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 4d0322e..f87b7bc 100644 --- a/README.md +++ b/README.md @@ -38,13 +38,13 @@ test(); ```javascript class ExampleClassWithCustomMethodName extends Callable { - constructor(){ - // in super provide object method`s name which will be used when calling object - super('myMethod') - } - myMethod(arg){ - return arg - } + constructor(){ + // in super provide object method`s name which will be used when calling object + super('myMethod') + } + myMethod(arg){ + return arg + } } ``` @@ -57,7 +57,7 @@ import Callable from "callable-instance"; const callableObject = Callable.from( { test: "test", [ Callable.CALL ]() { - return this.test; + return this.test; }, } ); @@ -74,9 +74,9 @@ import Callable from "callable-instance"; const callableObject = Callable.from( { test: "test", - getTest() { - return this.test; - }, + getTest() { + return this.test; + }, }, "getTest" ); // second parameter is optional method`s name which will be used when calling object // but it is important to also provide method property when cloning using spread + Callable.from @@ -104,24 +104,24 @@ interface IExampleClass { } // implements is optional but advised https://www.typescriptlang.org/docs/handbook/interfaces.html class ExampleClass extends Callable implements IExampleClass { - constructor(){ - super() - } - [Callable.CALL](arg: string){ - return arg - } + constructor(){ + super() + } + [Callable.CALL](arg: string){ + return arg + } } ``` 2. **Using function type** ```typescript class ExampleClass extends Callable<(arg: string) => string> { - constructor(){ - super() - } - [Callable.CALL](arg: string){ - return arg - } + constructor(){ + super() + } + [Callable.CALL](arg: string){ + return arg + } } ``` @@ -129,12 +129,12 @@ class ExampleClass extends Callable<(arg: string) => string> { ```typescript // easiest way for typing Callable class ExampleClass extends Callable { - constructor(){ - super() - } - [Callable.CALL](arg: string){ - return arg - } + constructor(){ + super() + } + [Callable.CALL](arg: string){ + return arg + } } ``` > **_NOTE:_** For function overload or generics use Interface or Function variant. From 8e07330dc6d27a899186a8119145f64819d2b8d4 Mon Sep 17 00:00:00 2001 From: pabadm Date: Sun, 4 Feb 2024 08:36:24 +0100 Subject: [PATCH 58/60] improved typings --- lib/index.d.ts | 6 ++++++ test/classGeneric.ts | 5 ++--- test/funcGeneric.ts | 8 ++++---- test/interfaceGeneric.ts | 16 ++++------------ test/overrideCall.ts | 2 +- tsconfig.json | 10 +++++----- 6 files changed, 22 insertions(+), 25 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index 4b71ae4..ad49c83 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -16,6 +16,7 @@ type ExtractFuncFromInterface< /** * call signature in interface type e.g {(...args: any): any} is considered function type + * * call signature in interface type combined with other properties e.g {(...args: any): any, property: any} is considered interface type. */ type ExtractFuncFromFuncType = Omit< @@ -30,6 +31,7 @@ interface CloneFuncFromClass { * For TS generics and function overload support use interface or function type for Callable */ (...args: Parameters[P]>): ReturnType[P]>; + prototype: any; } type ExtractFunc< @@ -72,12 +74,16 @@ interface CallableConstructor { C, typeof CALL >; + + readonly prototype: Callable; } export = Callable; declare const Callable: CallableConstructor; +interface Callable extends Function {} + declare namespace Callable { export type OverrideCall = { new < diff --git a/test/classGeneric.ts b/test/classGeneric.ts index d8ac2a6..4c6c54d 100644 --- a/test/classGeneric.ts +++ b/test/classGeneric.ts @@ -2,7 +2,6 @@ import { expectType } from "ts-expect"; import Callable, { OverrideCall } from "callable-instance"; // TESTS FOR CLASS-TYPE GENERICS - class RepeaterWithClassGeneric extends Callable< typeof RepeaterWithClassGeneric, "go" @@ -35,7 +34,7 @@ describe("Callable With Class Generic and custom property (TypeScript)", functio it("is an instance of Repeater", function () { expectType(new RepeaterWithClassGeneric(5)); - expectType>(new RepeaterWithClassGeneric(5)); + expectType(new RepeaterWithClassGeneric(5)); expectType(new RepeaterWithClassGeneric(5)); expectType(new RepeaterWithClassGeneric(5)); }); @@ -73,7 +72,7 @@ describe("Callable With Class Override Generic and custom property (TypeScript)" it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class expectType(new RepeaterWithClassOverride()); - expectType>(new RepeaterWithClassOverride()); + expectType(new RepeaterWithClassOverride()); expectType(new RepeaterWithClassOverride()); expectType(new RepeaterWithClassOverride()); }); diff --git a/test/funcGeneric.ts b/test/funcGeneric.ts index 25ef9b7..180e134 100644 --- a/test/funcGeneric.ts +++ b/test/funcGeneric.ts @@ -31,7 +31,7 @@ describe("Callable With Func Generic and custom property (TypeScript)", function it("is an instance of Repeater", function () { expectType(new RepeaterWithFuncGeneric(5)); - expectType>(new RepeaterWithFuncGeneric(5)); + expectType(new RepeaterWithFuncGeneric(5)); expectType(new RepeaterWithFuncGeneric(5)); expectType(new RepeaterWithFuncGeneric(5)); }); @@ -81,7 +81,7 @@ describe("Callable With Func overload Generic and custom property (TypeScript)", it("is an instance of Repeater", function () { expectType(new RepeaterWithFuncOverload(5)); - expectType>(new RepeaterWithFuncOverload(5)); + expectType(new RepeaterWithFuncOverload(5)); expectType(new RepeaterWithFuncOverload(5)); expectType(new RepeaterWithFuncOverload(5)); }); @@ -119,7 +119,7 @@ describe("Callable With TS Func Override Generic and custom property (TypeScript it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class expectType(new RepeaterWithFuncOverride()); - expectType>(new RepeaterWithFuncOverride()); + expectType(new RepeaterWithFuncOverride()); expectType(new RepeaterWithFuncOverride()); expectType(new RepeaterWithFuncOverride()); }); @@ -164,7 +164,7 @@ describe("Callable With Generic Function Generic and custom property (TypeScript it("is an instance of Repeater", function () { // is not passed because for typescript OverrideCall is other class expectType(new RepeaterWithGenericFunc(23)); - expectType>(new RepeaterWithGenericFunc(23)); + expectType(new RepeaterWithGenericFunc(23)); expectType(new RepeaterWithGenericFunc(23)); expectType(new RepeaterWithGenericFunc(23)); }); diff --git a/test/interfaceGeneric.ts b/test/interfaceGeneric.ts index 0326a90..729a7eb 100644 --- a/test/interfaceGeneric.ts +++ b/test/interfaceGeneric.ts @@ -42,9 +42,7 @@ describe("Callable With Interface Generic and custom property (TypeScript)", fun expectType( new RepeaterWithInterfaceGeneric(5) ); - expectType>( - new RepeaterWithInterfaceGeneric(5) - ); + expectType(new RepeaterWithInterfaceGeneric(5)); expectType(new RepeaterWithInterfaceGeneric(5)); expectType(new RepeaterWithInterfaceGeneric(5)); }); @@ -101,9 +99,7 @@ describe("Callable With TS Interface Overload Generic and custom property (TypeS expectType( new RepeaterWithInterfaceOverload(5) ); - expectType>( - new RepeaterWithInterfaceOverload(5) - ); + expectType(new RepeaterWithInterfaceOverload(5)); expectType(new RepeaterWithInterfaceOverload(5)); expectType(new RepeaterWithInterfaceOverload(5)); }); @@ -153,9 +149,7 @@ describe("Callable With Interface override Generic and custom property (TypeScri expectType( new RepeaterWithInterfaceOverride() ); - expectType>( - new RepeaterWithInterfaceOverride() - ); + expectType(new RepeaterWithInterfaceOverride()); expectType(new RepeaterWithInterfaceOverride()); expectType(new RepeaterWithInterfaceOverride()); }); @@ -210,9 +204,7 @@ describe("Callable With Generic Interface Generic and custom property (TypeScrip expectType( new RepeaterWithGenericInterface() ); - expectType>( - new RepeaterWithGenericInterface() - ); + expectType(new RepeaterWithGenericInterface()); expectType(new RepeaterWithGenericInterface()); expectType(new RepeaterWithGenericInterface()); }); diff --git a/test/overrideCall.ts b/test/overrideCall.ts index ef8c09b..fcbcc7f 100644 --- a/test/overrideCall.ts +++ b/test/overrideCall.ts @@ -36,7 +36,7 @@ class Extended extends (MyClass as OverrideCall)< public newPublicProperty = "public"; public readonly newReadonlyProperty = "readonly" as const; - [Callable.CALL]() { + private [Callable.CALL]() { return "str"; } } diff --git a/tsconfig.json b/tsconfig.json index fc3f52d..5ca2b6d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { - "compilerOptions": { - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - } -} \ No newline at end of file + "compilerOptions": { + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + } +} From 831fa32200b1d6304f793de9c1b6413bd50af7d2 Mon Sep 17 00:00:00 2001 From: pabadm Date: Sun, 4 Feb 2024 09:05:33 +0100 Subject: [PATCH 59/60] Fake key in Callable type to distinguish Callable and Function type --- lib/index.d.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index ad49c83..8d50037 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -34,15 +34,17 @@ interface CloneFuncFromClass { prototype: any; } +declare const CALLABLE_TS_KEY: unique symbol; + type ExtractFunc< C extends BaseClass | BaseFunc | BaseInterface, P extends BaseProperty > = C extends BaseClass - ? CloneFuncFromClass + ? CloneFuncFromClass & { [CALLABLE_TS_KEY]: never } : C extends BaseFunc - ? ExtractFuncFromFuncType + ? ExtractFuncFromFuncType & { [CALLABLE_TS_KEY]: never } : C extends BaseInterface - ? ExtractFuncFromInterface + ? ExtractFuncFromInterface & { [CALLABLE_TS_KEY]: never } : never; type PickProperties> = { @@ -82,7 +84,12 @@ export = Callable; declare const Callable: CallableConstructor; -interface Callable extends Function {} +interface Callable extends Function { + /** + * Forcing typescript to distinguish Function and Callable types + */ + [CALLABLE_TS_KEY]: never; +} declare namespace Callable { export type OverrideCall = { From ed14d23342dff4df4c279cc63e684cd8e6abc72c Mon Sep 17 00:00:00 2001 From: pabadm Date: Sun, 4 Feb 2024 09:16:01 +0100 Subject: [PATCH 60/60] removed prototype field from CloneFuncFromClass --- lib/index.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index 8d50037..7701a55 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -31,7 +31,6 @@ interface CloneFuncFromClass { * For TS generics and function overload support use interface or function type for Callable */ (...args: Parameters[P]>): ReturnType[P]>; - prototype: any; } declare const CALLABLE_TS_KEY: unique symbol;