Problem
@tko/lifecycle uses a mixin pattern (LifeCycle.mixInto()) that creates hidden dependencies and doesn't compose well. The mixin approach is an older pattern that makes the type system harder to reason about.
Proposal
- Keep the disposal tracking — subscription tracking,
anchorTo() cascading disposal, addDisposable() are all genuinely useful
- Add
Symbol.dispose / Symbol.asyncDispose — so TKO objects work with TC39 using declarations (ES2024+)
- Deprecate
LifeCycle.mixInto() — classes should extend or compose directly
- Composition over inheritance — offer the disposal tracker as a composable utility, not a mixin
Scope
Touches multiple packages:
@tko/lifecycle — the core change
@tko/bind — BindingHandler extends LifeCycle
@tko/utils.component — ComponentABC extends LifeCycle
@tko/utils.jsx — JsxObserver extends LifeCycle
@tko/binding.component — uses lifecycle methods
@tko/builder — imports LifeCycle
Current usage of lifecycle methods across the codebase
subscribe() — tracked observable subscriptions
computed() — tracked computed creation
addEventListener() — tracked DOM listeners with auto-cleanup
anchorTo() — hierarchical parent-child disposal chains
addDisposable() — accepts any object with dispose()
dispose() — tears down all tracked resources
Problem
@tko/lifecycleuses a mixin pattern (LifeCycle.mixInto()) that creates hidden dependencies and doesn't compose well. The mixin approach is an older pattern that makes the type system harder to reason about.Proposal
anchorTo()cascading disposal,addDisposable()are all genuinely usefulSymbol.dispose/Symbol.asyncDispose— so TKO objects work with TC39usingdeclarations (ES2024+)LifeCycle.mixInto()— classes should extend or compose directlyScope
Touches multiple packages:
@tko/lifecycle— the core change@tko/bind— BindingHandler extends LifeCycle@tko/utils.component— ComponentABC extends LifeCycle@tko/utils.jsx— JsxObserver extends LifeCycle@tko/binding.component— uses lifecycle methods@tko/builder— imports LifeCycleCurrent usage of lifecycle methods across the codebase
subscribe()— tracked observable subscriptionscomputed()— tracked computed creationaddEventListener()— tracked DOM listeners with auto-cleanupanchorTo()— hierarchical parent-child disposal chainsaddDisposable()— accepts any object withdispose()dispose()— tears down all tracked resources