mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-11-25 09:51:01 -05:00
Added a possibility to make objects callable
This commit is contained in:
parent
7882738bce
commit
d263c15393
2 changed files with 96 additions and 0 deletions
47
src/utils/functions/callable.ts
Normal file
47
src/utils/functions/callable.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* A symbol representing the `call` function of a {@link Callable} object.
|
||||||
|
*/
|
||||||
|
export const CALL = Symbol.for("call");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an object, which can be converted into a {@link Callable} one.
|
||||||
|
*/
|
||||||
|
interface SemiCallable {
|
||||||
|
/**
|
||||||
|
* A method that should be invoked, when an object is used as a function.
|
||||||
|
*/
|
||||||
|
[CALL](...args: unknown[]): unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an object, which can be called like a function.
|
||||||
|
*
|
||||||
|
* @template T - The type of the underlying object.
|
||||||
|
*/
|
||||||
|
export type Callable<T extends SemiCallable> = T & {
|
||||||
|
/**
|
||||||
|
* Redirects a call to the {@link CALL} function.
|
||||||
|
*/
|
||||||
|
(...args: Parameters<T[typeof CALL]>): ReturnType<T[typeof CALL]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes an object callable.
|
||||||
|
*
|
||||||
|
* @template T - The type of the object.
|
||||||
|
* @param obj - The object to make callable.
|
||||||
|
*
|
||||||
|
* @returns A new {@link Callable} object with the same properties as the original one, but which can be called like a function.
|
||||||
|
*/
|
||||||
|
export function makeCallable<T extends SemiCallable>(obj: T): Callable<T> {
|
||||||
|
/**
|
||||||
|
* Redirects a call to the {@link CALL} function.
|
||||||
|
*/
|
||||||
|
function call(...args: unknown[]): unknown {
|
||||||
|
return (call as unknown as T)[CALL](...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(call, obj);
|
||||||
|
Object.setPrototypeOf(call, Object.getPrototypeOf(obj));
|
||||||
|
return call as unknown as Callable<T>;
|
||||||
|
}
|
49
tests/unit/utils/functions/callable.spec.ts
Normal file
49
tests/unit/utils/functions/callable.spec.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { CALL, makeCallable } from "@/utils/functions/callable";
|
||||||
|
|
||||||
|
describe("makeCallable", () => {
|
||||||
|
test("makes an object callable", () => {
|
||||||
|
const obj = {
|
||||||
|
[CALL]: (a: number, b: number) => a + b,
|
||||||
|
};
|
||||||
|
|
||||||
|
const callable = makeCallable(obj);
|
||||||
|
|
||||||
|
expect(callable(1, 2)).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("preserves object properties", () => {
|
||||||
|
const obj = {
|
||||||
|
foo: 42,
|
||||||
|
|
||||||
|
[CALL](): number {
|
||||||
|
return this.foo;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const callable = makeCallable(obj);
|
||||||
|
|
||||||
|
expect(callable()).toBe(42);
|
||||||
|
expect(callable.foo).toBe(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("preserves object prototype", () => {
|
||||||
|
class FooClass {
|
||||||
|
foo: number;
|
||||||
|
|
||||||
|
constructor(foo: number) {
|
||||||
|
this.foo = foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CALL](): number {
|
||||||
|
return this.foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = new FooClass(42);
|
||||||
|
const callable = makeCallable(obj);
|
||||||
|
|
||||||
|
expect(callable).toBeInstanceOf(FooClass);
|
||||||
|
expect(callable.foo).toBe(42);
|
||||||
|
expect(callable()).toBe(42);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue