Skip to content

Instantly share code, notes, and snippets.

@petsel
Last active July 22, 2025 20:37
Show Gist options
  • Select an option

  • Save petsel/fcba946d3f4bff470c4f1b6b4f3103f7 to your computer and use it in GitHub Desktop.

Select an option

Save petsel/fcba946d3f4bff470c4f1b6b4f3103f7 to your computer and use it in GitHub Desktop.
/**
* Module: function.bind.with-exotic-object-slots
*
* TypeScript declaration for a custom override of `Function.prototype.bind`
* that stores ECMAScript-like internal slot metadata as Symbol-based properties
* on bound functions.
*
* Inspired by ECMAScript internal slots:
* - `[[BoundTargetFunction]]`
* - `[[BoundThis]]`
* - `[[BoundArguments]]`
*
* Specification References:
* - https://tc39.es/ecma262/#bound-function-exotic-object
* - https://tc39.es/ecma262/#sec-function.prototype.bind
*/
declare module 'function.bind.with-exotic-object-slots' {
/**
* Public symbols used to access internal metadata on bound functions.
* These mimic ECMAScript internal slot names (wrapped in `[[ ]]`).
*/
/**
* Symbolic key representing `[[TargetFunction]]` (equal to the Chrome-Dev-Tool specific implementation).
* Equivalent to ECMAScript's internal `[[BoundTargetFunction]]` slot.
*
* Use to access a bound function's original source/function:
* `boundFn[boundFunctionKey]`
*/
export const boundFunctionKey: unique symbol;
/**
* Symbolic key representing `[[BoundThis]]`.
* Does equal ECMAScript's internal `[[BoundThis]]` slot.
*
* Use to access a bound function's `thisArg`:
* `boundFn[boundThisArgKey]`
*/
export const boundThisArgKey: unique symbol;
/**
* Symbolic key representing `[[BoundArgs]]` (equal to the Chrome-Dev-Tool specific implementation).
* Equivalent to ECMAScript's internal `[[BoundArguments]]` slot.
*
* Use to access a bound function's partially applied arguments:
* `boundFn[boundArgsKey]`
*/
export const boundArgsKey: unique symbol;
/**
* Applies the custom `bind` override to `Function.prototype.bind`
* and injects metadata-augmented binding behavior.
*/
export function applyPrototypeChange(): void;
/**
* Restores the native `Function.prototype.bind` behavior.
*/
export function restorePrototypeDefault(): void;
/**
* Generic utility `bind` method.
*
* An alternative static variant to the custom `bind` override
* of the `Function.prototype.bind` method which creates bound
* functions with metadata.
*
* @template {(...args: any[]) => any} T
* @param {T} targetFct - The function to bind.
* @param {any} thisArg - The `this` value to bind.
* @param {...Parameters<T>} args - The arguments to pre-bind.
* @returns {T} A new bound function, decorated with symbol-keyed metadata.
*/
export function bind<T extends (...args: any[]) => any>(
targetFct: T,
thisArg: any,
...args: Parameters<T>
): T;
}
/**
* Global augmentation of the `Function` prototype to
* reflect the symbol-keyed metadata installed by the
* custom `bind` override.
* These properties exist only on functions returned
* by the prototypal custom `bind` method.
*/
interface Function {
/**
* A reference to the original (unbound) target function.
* Equivalent to ECMAScript's internal `[[BoundTargetFunction]]` slot.
*/
[Symbol.for('[[TargetFunction]]')]?: Function;
/**
* A reference to the `thisArg` value bound during `bind(...)`.
* Does equal ECMAScript's internal `[[BoundThis]]` slot.
*/
[Symbol.for('[[BoundThis]]')]?: unknown;
/**
* A reference to the partially applied arguments bound during `bind(...)`.
* Equivalent to ECMAScript's internal `[[BoundArguments]]` slot.
*/
[Symbol.for('[[BoundArgs]]')]?: unknown[];
}
// see ... [https://tc39.es/ecma262/#bound-function-exotic-object]
// see ... [https://tc39.es/ecma262/#sec-function.prototype.bind]
const functionPrototype = Function.prototype;
const builtInBind = functionPrototype.bind;
/** @type {PropertyDescriptor} */
const bindFunctionDescriptor =
Object.getOwnPropertyDescriptor(functionPrototype, 'bind');
/** @type {PropertyDescriptor} */
const bindFunctionNameDescriptor =
Object.getOwnPropertyDescriptor(builtInBind, 'name');
/** @type {PropertyDescriptor} */
const slotDescriptorOptions = {
enumerable: false,
writable: false,
configurable: false,
};
/**
* Symbolic key representing `[[TargetFunction]]` (equal to the Chrome-Dev-Tool specific implementation).
* A reference to the original (unbound) target function.
* Equivalent to ECMAScript's internal `[[BoundTargetFunction]]` slot.
*
* Use to access a bound function's original source/function:
* `boundFn[boundFunctionKey]`
*
* @constant
* @type {unique symbol}
*/
export const boundFunctionKey = Symbol.for('[[TargetFunction]]'); // Symbol.for('[[BoundTargetFunction]]')
/**
* Symbolic key representing `[[BoundThis]]`.
* A reference to the `thisArg` value bound during `bind(...)`.
* Does equal ECMAScript's internal `[[BoundThis]]` slot.
*
* Use to access a bound function's `thisArg`:
* `boundFn[boundThisArgKey]`
*
* @constant
* @type {unique symbol}
*/
export const boundThisArgKey = Symbol.for('[[BoundThis]]'); // Symbol.for('[[BoundThis]]');
/**
* Symbolic key representing `[[BoundArgs]]` (equal to the Chrome-Dev-Tool specific implementation).
* A reference to the partially applied arguments bound during `bind(...)`.
* Equivalent to ECMAScript's internal `[[BoundArguments]]` slot.
*
* Use to access a bound function's partially applied arguments:
* `boundFn[boundArgsKey]`
*
* @constant
* @type {unique symbol}
*/
export const boundArgsKey = Symbol.for('[[BoundArgs]]'); // Symbol.for('[[BoundArguments]]');
/**
* Creates a bound function with slot-like metadata.
*
* @this {Function}
* @param {*} thisArg - The value to bind as `this`.
* @param {...*} args - Arguments to bind.
* @returns {Function} A new bound function with internal metadata.
*/
function bind(thisArg, ...args) {
'hide source';
const targetFct = /** @type {Function} */ (this);
const bound = builtInBind.call(targetFct, thisArg, ...args);
Object.defineProperty(bound, boundFunctionKey, { ...slotDescriptorOptions, value: targetFct });
Object.defineProperty(bound, boundThisArgKey, { ...slotDescriptorOptions, value: thisArg });
Object.defineProperty(bound, boundArgsKey, { ...slotDescriptorOptions, value: [...args] });
return bound;
}
const proceed = bind;
Object.defineProperty(bind, 'name', { ...bindFunctionNameDescriptor, value: 'bind' });
/**
* Generic utility `bind` method.
*
* An alternative static variant to the custom `bind` override
* of the `Function.prototype.bind` method which creates bound
* functions with metadata.
*
* @template {(...args: any[]) => any} T
* @param {T} targetFct - The function to bind.
* @param {*} thisArg - The `this` value to bind.
* @param {...Parameters<T>} args - The arguments to pre-bind.
* @returns {T} A new bound function, decorated with symbol-keyed metadata.
*/
export function bind(targetFct, thisArg, ...args) {
'hide source';
return /** @type {T} */ (
proceed.call(targetFct, thisArg, ...args)
);
}
/**
* Applies the custom `bind` override to `Function.prototype.bind`
* and injects metadata-augmented binding behavior.
*/
export function applyPrototypeChange() {
Object.defineProperty(functionPrototype, 'bind', {
...bindFunctionDescriptor,
value: bind,
});
}
/**
* Restores the native `Function.prototype.bind` behavior.
*/
export function restorePrototypeDefault() {
Object.defineProperty(functionPrototype, 'bind', {
...bindFunctionDescriptor,
value: builtInBind,
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment