UNPKG

@tldraw/utils

Version:

tldraw infinite canvas SDK (private utilities).

8 lines (7 loc) 5.41 kB
{ "version": 3, "sources": ["../../src/lib/bind.ts"], "sourcesContent": ["/*!\n * MIT License: https://github.com/NoHomey/bind-decorator/blob/master/License\n * Copyright (c) 2016 Ivo Stratev\n */\n\nimport { assert } from './control'\n\n/**\n * Decorator that binds a method to its class instance (legacy stage-2 TypeScript decorators).\n * When applied to a class method, ensures `this` always refers to the class instance,\n * even when the method is called as a callback or event handler.\n *\n * @param target - The prototype of the class being decorated\n * @param propertyKey - The name of the method being decorated\n * @param descriptor - The property descriptor for the method being decorated\n * @returns The modified property descriptor with bound method access\n * @example\n * ```typescript\n * class MyClass {\n * name = 'example';\n *\n * @bind\n * getName() {\n * return this.name;\n * }\n * }\n *\n * const instance = new MyClass();\n * const callback = instance.getName;\n * console.log(callback()); // 'example' (this is properly bound)\n * ```\n * @public\n */\nexport function bind<T extends (...args: any[]) => any>(\n\ttarget: object,\n\tpropertyKey: string,\n\tdescriptor: TypedPropertyDescriptor<T>\n): TypedPropertyDescriptor<T>\n\n/**\n * Decorator that binds a method to its class instance (TC39 decorators standard).\n * When applied to a class method, ensures `this` always refers to the class instance,\n * even when the method is called as a callback or event handler.\n *\n * @param originalMethod - The original method being decorated\n * @param context - The decorator context containing metadata about the method\n * @example\n * ```typescript\n * class EventHandler {\n * message = 'Hello World';\n *\n * @bind\n * handleClick() {\n * console.log(this.message);\n * }\n * }\n *\n * const handler = new EventHandler();\n * document.addEventListener('click', handler.handleClick); // 'this' is properly bound\n * ```\n * @public\n */\nexport function bind<This extends object, T extends (...args: any[]) => any>(\n\toriginalMethod: T,\n\tcontext: ClassMethodDecoratorContext<This, T>\n): void\n\n/**\n * Universal decorator implementation that handles both legacy stage-2 and TC39 decorator formats.\n * Automatically detects the decorator format based on the number of arguments and binds the\n * decorated method to the class instance, preventing common `this` context issues.\n *\n * @param args - Either legacy decorator arguments (target, propertyKey, descriptor) or TC39 decorator arguments (originalMethod, context)\n * @returns Property descriptor for legacy decorators, or void for TC39 decorators\n * @example\n * ```typescript\n * // Works with both decorator formats\n * class Calculator {\n * multiplier = 2;\n *\n * @bind\n * multiply(value: number) {\n * return value * this.multiplier;\n * }\n * }\n *\n * const calc = new Calculator();\n * const multiplyFn = calc.multiply;\n * console.log(multiplyFn(5)); // 10 (this.multiplier is accessible)\n *\n * // Useful for event handlers and callbacks\n * setTimeout(calc.multiply, 100, 3); // 6\n * ```\n * @public\n */\nexport function bind(\n\t...args: // legacy stage-2 typescript decorators\n\t| [_target: object, propertyKey: string, descriptor: PropertyDescriptor]\n\t\t// TC39 decorators\n\t\t| [originalMethod: (...args: any[]) => any, context: ClassMemberDecoratorContext]\n): PropertyDescriptor | void {\n\tif (args.length === 2) {\n\t\tconst [originalMethod, context] = args\n\t\tcontext.addInitializer(function initializeMethod(this: any) {\n\t\t\tassert(Reflect.isExtensible(this), 'Cannot bind to a non-extensible class.')\n\t\t\tconst value = originalMethod.bind(this)\n\t\t\tconst ok = Reflect.defineProperty(this, context.name, {\n\t\t\t\tvalue,\n\t\t\t\twritable: true,\n\t\t\t\tconfigurable: true,\n\t\t\t})\n\t\t\tassert(ok, 'Cannot bind a non-configurable class method.')\n\t\t})\n\t} else {\n\t\tconst [_target, propertyKey, descriptor] = args\n\t\tif (!descriptor || typeof descriptor.value !== 'function') {\n\t\t\tthrow new TypeError(\n\t\t\t\t`Only methods can be decorated with @bind. <${propertyKey}> is not a method!`\n\t\t\t)\n\t\t}\n\n\t\treturn {\n\t\t\tconfigurable: true,\n\t\t\tget(this: any): any {\n\t\t\t\tconst bound = descriptor.value!.bind(this)\n\t\t\t\t// Credits to https://github.com/andreypopp/autobind-decorator for memoizing the result of bind against a symbol on the instance.\n\t\t\t\tObject.defineProperty(this, propertyKey, {\n\t\t\t\t\tvalue: bound,\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t\twritable: true,\n\t\t\t\t})\n\t\t\t\treturn bound\n\t\t\t},\n\t\t}\n\t}\n}\n"], "mappings": "AAAA;AAAA;AAAA;AAAA;AAKA,SAAS,cAAc;AA0FhB,SAAS,QACZ,MAIyB;AAC5B,MAAI,KAAK,WAAW,GAAG;AACtB,UAAM,CAAC,gBAAgB,OAAO,IAAI;AAClC,YAAQ,eAAe,SAAS,mBAA4B;AAC3D,aAAO,QAAQ,aAAa,IAAI,GAAG,wCAAwC;AAC3E,YAAM,QAAQ,eAAe,KAAK,IAAI;AACtC,YAAM,KAAK,QAAQ,eAAe,MAAM,QAAQ,MAAM;AAAA,QACrD;AAAA,QACA,UAAU;AAAA,QACV,cAAc;AAAA,MACf,CAAC;AACD,aAAO,IAAI,8CAA8C;AAAA,IAC1D,CAAC;AAAA,EACF,OAAO;AACN,UAAM,CAAC,SAAS,aAAa,UAAU,IAAI;AAC3C,QAAI,CAAC,cAAc,OAAO,WAAW,UAAU,YAAY;AAC1D,YAAM,IAAI;AAAA,QACT,8CAA8C,WAAW;AAAA,MAC1D;AAAA,IACD;AAEA,WAAO;AAAA,MACN,cAAc;AAAA,MACd,MAAoB;AACnB,cAAM,QAAQ,WAAW,MAAO,KAAK,IAAI;AAEzC,eAAO,eAAe,MAAM,aAAa;AAAA,UACxC,OAAO;AAAA,UACP,cAAc;AAAA,UACd,UAAU;AAAA,QACX,CAAC;AACD,eAAO;AAAA,MACR;AAAA,IACD;AAAA,EACD;AACD;", "names": [] }