{"id":22344483,"url":"https://github.com/ayonli/js-magic","last_synced_at":"2025-07-21T21:36:47.545Z","repository":{"id":57283313,"uuid":"152283834","full_name":"ayonli/js-magic","owner":"ayonli","description":"JavaScript magic methods support.","archived":false,"fork":false,"pushed_at":"2023-09-22T11:54:08.000Z","size":60,"stargazers_count":13,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-04T03:43:25.728Z","etag":null,"topics":["javascript","magic-methods"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ayonli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-10-09T16:24:10.000Z","updated_at":"2024-06-15T07:45:27.000Z","dependencies_parsed_at":"2023-08-08T20:48:15.291Z","dependency_job_id":"6b333762-ae19-4572-9ee8-a673e4d61547","html_url":"https://github.com/ayonli/js-magic","commit_stats":null,"previous_names":["ayonli/js-magic","hyurl/js-magic"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/ayonli/js-magic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayonli%2Fjs-magic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayonli%2Fjs-magic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayonli%2Fjs-magic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayonli%2Fjs-magic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ayonli","download_url":"https://codeload.github.com/ayonli/js-magic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayonli%2Fjs-magic/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266384220,"owners_count":23921006,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-21T11:47:31.412Z","response_time":64,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","magic-methods"],"created_at":"2024-12-04T09:11:58.279Z","updated_at":"2025-07-21T21:36:47.525Z","avatar_url":"https://github.com/ayonli.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JS-Magic\n\n**JavaScript magic methods support.**\n\nWe know that ES6 brings the capability of Proxy that allows us observing an \nobject, and setters ang getters are build-in support in JavaScript, but if we \nneed to build those things every time, that's just mess and pain. With this \npackage, you can define setters and getters, along with other functions, right \nin the class definition itself, and when instantiating the class, the instance\nwill always have the the benefits of the magical calling functionalities.\n\nThis package is inspired by PHP magic methods, and currently supports these \nmethods: `__get`, `__set`, `__has`, `__delete`, `__invoke`. Other methods like \n`toString` and `toJSON` are built-in support in JavaScript.\n\n## Install\n\n```sh\nnpm i js-magic\n```\n\n### In Deno\n\nJust import this package directly:\n\n```ts\nimport { applyMagic } from \"https://deno.land/x/js_magic/index.ts\";\n```\n\n## Example\n\n```typescript\n// This example is coded in TypeScript, be aware of the difference between TS \n// and JS. All the magic methods are optional, but here I'll show all the usage \n// of them.\n\nimport { applyMagic, MagicalClass } from \"js-magic\";\n\n@applyMagic\nexport class Car implements MagicalClass {\n    name!: string;\n    wheels?: number;\n\n    constructor(name?: string, wheels?: number) {\n        if (name !== undefined) this.name = name;\n        if (wheels !== undefined) this.wheels = wheels;\n    }\n\n    /**\n     * If a property doesn't exist, returns `null` instead of `undefined`, and \n     * if the property is 'name', returns it according to the class name plus \n     * 'Instance'. \n     */\n    __get(prop: string | symbol): any {\n        return prop in this ? this[prop]\n            : (prop == \"name\" ? this.constructor.name + \" Instance\" : null);\n    }\n\n    /** If the property is name, appends it with 'Instance'. */\n    __set(prop: string | symbol, value: any): void {\n        this[prop] = prop == \"name\" ? value + \" Instance\" : value;\n    }\n\n    /**\n     * Ignores the properties starts with '__', and always returns `true` when \n     * testing 'name'.\n     */\n    __has(prop: string | symbol): boolean {\n        return (typeof prop != \"string\" || prop.slice(0, 2) != \"__\")\n            \u0026\u0026 (prop in this || prop == \"name\");\n    }\n\n    /** If the property starts with '__' or is 'name', DO NOT delete. */\n    __delete(prop: string | symbol): void {\n        if (prop.slice(0, 2) == \"__\" || prop == \"name\") return;\n        delete this[prop];\n    }\n\n    /**\n     * This method will be called when the class is invoked as a function. You \n     * may be a little confused since being told that ES6 class cannot be called\n     * as function, AKA without `new` operator, well, when using this package, \n     * you CAN.\n     * \n     * NOTE: prior to v1.2, __invoke without `static` modifier is permitted, but\n     * it's now deprecated, always add `static` instead.\n     */\n    static __invoke(...args: any[]): any {\n        return \"invoking Car as a function\";\n    }\n}\n```\n\n## How It Works?\n\nThe decorator `applyMagic` is a function that returns a highly-customized ES5 \npseudo-class, it will replace the original class, so that when instantiating,\nthe  magic methods will be auto-applied to the instance wrapped by a `Proxy`.\nSince `applyMagic` is a function, so if you're coding in JavaScript without\ndecorator support, you can manually call it to generate the wrapping class and\nassign to the old one. Like this:\n\n```javascript\nimport { applyMagic } from \"js-magic\";\n\nclass Car {\n    // ...\n}\n\nCar = applyMagic(Car);\n```\n\nSince the returned class is wrapped in ES5 style, so that it allows you calling \nit as a function, where the `__invoke` method will called under the hood.\n\n## Support of Inheritance\n\nThis package also supports native inheritance, allows you inheriting the magical\ncalling functionalities from a super class to sub-classes. Also you can rewrite\nthe magic methods in the sub-class, and call the super's via `super` keyword.\n\nNOTE: this feature DOESN'T work with `__invoke`, unless using `applyMagic` on\nthe sub-class as well.\n\n## Support of Objects Other Than Class\n\nSince v1.1, this package also supports other objects other than class, if\ncalling `applyMagic` on a non-function object, it will returns a proxy of the\noriginal object that supports magic functions. Moreover, if you want this\nfeature be apply to a function, you can pass the second argument `proxyOnly` to\n`applyMagic`, and it will not treat the function as a potential class. \n\n## Additional Symbols\n\nThis package also provides symbols according to the magic method names (`__get`, \n`__set`, `__has`, `__delete`, `__invoke`), you can use them if you want to hide \nthe methods from IDE IntelliSense, but generally they are not common used.\n\n## Supported Environments\n\nAny environment that supports ES6 `Proxy` will work with this package perfectly, \ngenerally, NodeJS `6.0+`, Deno and modern browsers (`IE` aside) should support\n`Proxy` already.\n\nIn browsers, if you're not using any module resolution, access the global \nvariable `window.magic` instead.\n\n## More Examples\n\nFor more examples, please check out the [Test](./test.js).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fayonli%2Fjs-magic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fayonli%2Fjs-magic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fayonli%2Fjs-magic/lists"}