{"id":18782969,"url":"https://github.com/webex/ts-events","last_synced_at":"2026-03-06T16:03:15.782Z","repository":{"id":161832811,"uuid":"617057596","full_name":"webex/ts-events","owner":"webex","description":null,"archived":false,"fork":false,"pushed_at":"2025-12-10T08:40:46.000Z","size":313,"stargazers_count":2,"open_issues_count":3,"forks_count":1,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-12-10T11:21:10.480Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/webex.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-03-21T16:05:23.000Z","updated_at":"2025-11-24T13:29:47.000Z","dependencies_parsed_at":"2024-12-29T11:32:02.038Z","dependency_job_id":"445860e3-1af3-44f4-b578-f8178c17f05c","html_url":"https://github.com/webex/ts-events","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/webex/ts-events","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webex%2Fts-events","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webex%2Fts-events/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webex%2Fts-events/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webex%2Fts-events/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/webex","download_url":"https://codeload.github.com/webex/ts-events/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webex%2Fts-events/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30184885,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T14:42:24.748Z","status":"ssl_error","status_checked_at":"2026-03-06T14:42:14.925Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2024-11-07T20:37:39.253Z","updated_at":"2026-03-06T16:03:15.767Z","avatar_url":"https://github.com/webex.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ts-events\n\nA library for subscribing to and emitting type-safe events using a familiar, javascript-style API:\n\n```typescript\nmyClass.on('eventName', (value: number) { ... });\n// Error: handler signature is wrong\nmyClass.on('eventName', (value: boolean) { ... });\n// Error: event name is wrong\nmyClass.on('eventNameWithATypo', (value: number) { ... });\n```\n\n### Why does this library exist?\n\nThere exist multiple typesafe event emitter libraries for typescript (we were previously using [this one](https://github.com/andywer/typed-emitter) and\nalso tried [this one](https://github.com/binier/tiny-typed-emitter)), so why is this library necessary? \n\nWhen using \nexisting solutions, we were unable to implement an event 'hierarchy': where a class inheritance chain could have\neach class defining appropriate events for their level.  Some links to issues others have had around this can\nbe found [here](https://github.com/binier/tiny-typed-emitter/issues/6#ref-commit-1a5053b), and [here](https://stackoverflow.com/a/71272663/612493).\nPlaygrounds with our own attempts to make the above libraries work can be found below:\n\n1. An attempt still using EventEmitter from typed-emitter, but adding generics to allow subclasses to add events: [Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAFQJ5gKYBMCiJgxqqAGjgG85MA3VAOxgFkBDMOAXzgDMoIQ4ByGFBgC0qHHgK8A3AChQkWKXJVa2XPihwGAZ3KZWHLj16plMLVOnSAxgBttOyjRirxUADwI4qAB75q6B1NGMAA+L18aALgACkw9bUVpOGS4alQAdzgPTx8-KMdaYJDogEoALkRBLDF1bJCZFhKPEJIWSwB6drgABQYoJ2kBNER0iAL4AF5SJJT2YFQbdArqAFcQACMCaTbZWgJ2BitUHr6ncZ1cyMCnYOmUuABtY1MAeTTeAF0K6JMnZbXNlASnAJmEKBBgOgZPcnr9aAhRp9vnCYBUEWNTMDQXBwZDth0ugB1Y5pDBwGAQOArLTHBhwADmNAIwCscAAFgRUMQKXAtCswPJ4HSrGzgIs4LZ7OE8sBqPSTv0VKYdDyGOh0HBcDoIOlqF5lQA6ax2LQ6XqKmAAYRNWg80quCrOyrCl381xUNQIzTuHGA-XGb1QpR9yU6cAAkvB0BBUFpqPxeahjrwACwAJl4mh0dJADBgIvZnKN9xgoq0BtEuGizycgd4xHTJUk93uMzaOxoazgltFi3GADkGCBYyGlE50SC+Cj0bx8bt1Acjt3e1hlfa3WPCkxRw8e2LV05B8Py+N0V8YhQGDYVqh-hsCFiwRD0NsgA)\n1. A slightly different approach using the tiny-typed-emitter lib: [Playground](https://www.typescriptlang.org/play?ssl=28\u0026ssc=1\u0026pln=28\u0026pc=2#code/JYWwDg9gTgLgBAFQJ5gKYBMCiJgxqqAGjgG85MA3VAOxgFkBDMOAXzgDMoIQ4ByGFBgC0qHHgK8A3AChQkWKXJVa2XPihwGAZ3KZWHLj16plMLVOnSAxgBttOyjRirxUADwI4qAB75q6B1NGMAA+L18aALgACkw9bUVpOGS4alQAdzgPTx8-KMdaYJDogEoALkRBLDF1bJCZFhKPEJIWSwB6drgASWotMGAoDDgAIyQ4GGBqJCEBNHQRGoIy6U64AAs8MC0yzoBzXHWAVxGAOitudpGp4AJ2yenZqsW1O5GbCBH2kG11dptgF8pugfKd0KczABiAAyAEZpHNUHBocAtH4CABlYB7agMGBHIZuaFhAC8iRScAA2nopnAANaoJAQdjIgC6FWipy5DCgex2cCO1Dp1Ag6WolNZJTgJLCguFouokjg0jaqy6AAUeU5LDQjjxNUMVKYAHIMECoHQkJIpAowADyaWlfBMTiEEDSvBVlim6nYDCsSINTltAGEGDYbCN-XTLdbkpSg0anKbzVpTraHah2TEKOGjqgKtQ9SMCFKZXAKBBgOgvdY7FodImYCH61oPOE8joUWiaJjsbj8YSECESd30VAsTi8QTUG45SKxSEwrlIoFg0t3E3Q+HI9GdAAyRBhMjKinsQaoDNpUqkOMUibrVGnUS4aJbk1mi3p0yZ4gAFgAJhKGQKTaFggA)\n\nAn additional downside of the above libraries is that, since they involve inheriting from the `EventEmitter` class, the `emit` method was\nexposed as part of the class' API; we'd like to be able to prevent outside entities from emitting a class' events.\n\nWhen developing this library, we had the following requirements:\n1. As much type safety as possible should be provided.  Referring to an invalid event name or passing a handler whose signature doesn't match \nshould fail at compile time.\n1. Class hierarchies should allow classes at each level to add events that make sense for them.\n1. We want to preserve the 'familiar' event subscription API: `class.on('eventName', \u003chandler\u003e)`.\n1. We don't want to have to manually implement the event subscription API on each class (minimize the need for boilerplate code).\n1. Classes should have the flexibility to decide whether or not a subclass can emit their events.\n\nThe implementation of this library accomplishes the above requirements, but not without tradeoffs.  A summary of what is required to\nuse this library can be found below.\n\n# Methodology\n\nThis library implements the behaviors described above via the use of a [mixin](https://www.typescriptlang.org/docs/handbook/mixins.html) and a helper type.\n\nThe mixin defines the event subscription APIs (`on`, `off`, `once`, etc.) and is generic around a type defining the event names and their handler signatures.  This is\nwhat provides the type safety when calling the event subscription methods.\nThe helper type helps make sure the resulting type (after adding the mixin) is defined as a _type_ not a _value_, such that it can be used just like a normal\nclass.\n\nGiven a class:\n```typescript\ninterface ParentEvents {\n  eventOne: TypedEvent\u003c(value: number) =\u003e void\u003e;\n}\n\nclass _Parent {\n    protected eventOne = new TypedEvent\u003c(value: number) =\u003e void\u003e();    \n}\n```\n\n***NOTE***: The property names between the events definition (`ParentEvents`) and the class (`_Parent`) ***must*** match.  I.e. it needs to be `eventOne` in both places.\n\nThe type that should be exposed can be generated like so:\n```typescript\n// Create and export the mixin type by adding the event subscription APIs\nexport const Parent = AddEvents\u003ctypeof _Parent, ParentEvents(_Parent);\n\n// Export it as a type as well, to avoid \"'Parent' refers to a value, but is being used as a type\".\nexport const Parent = _Parent \u0026 WithEventsDummyType(ParentEvents);\n```\nThis type (`Parent`) is what should be exported and used by other code (_not_ `_Parent`).\n\n\n## FAQ\n##### _Rather than requiring constraints on the type `U` in AddEvents via documentation, why not express them in the type system?_\n\nThis was attempted.  We originally borrowed a type defined in the EventEmitter libraries above, like so:\n\n```typescript\ntype EventMap = {\n  [key: string]: (...args: any[]) =\u003e void\n}\n```\nWhich could enforce that the events type always had keys that were strings and values which were functions. But unfortunately this will allow undefined event names to be passed without an error.  A playground illustrating this issue can be found [here](https://www.typescriptlang.org/play?ssl=19\u0026ssc=1\u0026pln=20\u0026pc=1#code/C4TwDgpgBAwg9gOwM7AE4FcDGw6qgXigQgHcoAKAOmoENUBzJALihoRAG0BdASgID4oAbwC+AbgBQoSFAASbACYAbCHkJVaDZq3bc++QQDc4ASwWTp0AKKGICYAFkaYAsIlQPUDgGsIIFiioJgj0XCzyCMqqEiISEsHAqgBmNJjWtvZIUBAAHomRWTZ2js5unkQ0ALYQLOQIVTVQgcH0+kamCgA07p409I3kfY0I6JUARqptUMZmMXFJ6AjYJohQAOomwAAWRZkAPAAqAEI0SNC5+QpZ8MhoWDionVAAqtl5dldQuyVg-OQnZxYx1OED4Qh6HlQEGA6FQCCgmCUpyyG223yyFw+WQB0HB5XKiD2AGk3pcsr4QHAki8ngdSViXhwiVw-hSWESnltFCpUECwbFyrFYhJEcioAB9AAKqiQqzxnnq1QCaBargARGM1ZJykMWCNxqpXAAWABMYnKEKgcxFiBQUGlqFl8MIqJ2GWASD2lipEodTqe6L+UpliB4khttygkEdq0IxDIfsQ5DDEmjTsoSYA5ENM09yIYaEp0MNRhNUFNRCmAPRVqAHLYmLJILZwdBKBRQCY6bKoVC4J5jdDAKCNhCZ4eYGjoehbYCpkMIDMIciZpJwOC5igVkRhoA).  This is something I'd\nlove to improve, so would be happy to learn of a solution here.\n\n# Development \n## Setup\n\n1. Run `yarn` to install dependencies.\n2. Run `yarn prepare` to prepare dependencies.\n3. Run `yarn watch` to build and watch for updates.\n4. Run `yarn test` to build, run tests, lint, and run test coverage.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebex%2Fts-events","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwebex%2Fts-events","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebex%2Fts-events/lists"}