garretpremo / rxjs-observed-decorator Goto Github PK
View Code? Open in Web Editor NEWExtremely simple class property decorator which ties a class variable to an RxJS Subject.
License: MIT License
Extremely simple class property decorator which ties a class variable to an RxJS Subject.
License: MIT License
Using the latest version of Typescript breaks how this decorator interacts with objects & their prototypes.
On initial function call, this decorator does an Object.defineProperty
on the object prototype, which is passed in as target
. Then, when a value is set on the object (this), it calls the prototype setter. Which in turn does a second Object.defineProperty
on the object itself (this). That second Object.defineProperty
is what allows individual objects to have individual BehaviorSubjects
, and individual values.
But now, with Typescript 4.9.4, this interaction is broken. Typescript is overwriting the object itself (this) with the object keys that are defined on the class. So, instead of calling the prototype methods, and having getters and setters on the this object, it is running into default defined fields, which are not observed.
// compile time
class MyClass {
@Observed() myString = "this is a string";
myString$: Observable<string>;
}
// run time
{
get myString(), // "this is a string"
set myString(),
get "myString$"(), // Observable<"this is a string">
set "myString$"(),
__proto__: {
get myString(), // null
set myString(),
get "myString$"(), // Observable<null>
set "myString$"(),
}
}
// compile time
class MyClass {
@Observed() myString = "this is a string";
myString$: Observable<string>;
}
// run time
{
myString: "this is a string",
"myString$": undefined,
__proto__: {
get myString(), // null
set myString(),
get "myString$"(), // Observable<null>
set "myString$"(),
}
}
Upgrade the dependencies of this project to the following. Then run build
& test
scripts.
"ts-node": "10.9.1",
"typescript": "4.9.4",
Using the @Observed({ type: 'subject' })
in version 1.x
is a rather clunky process, especially if that subject is of type: void
. This proposes adding a defined function for @Observed({ type: 'subject' })
, that can be called instead of manually setting an irrelevant next value. The function would be named as key + 'Event'
, similar to how we name the Observable as key + '$'
.
For the rest of this feature proposal, we can use the following example:
@Observed({ type: 'subject' }) doTheThing: void;
readonly doTheThing$: Observable<void>;
readonly doTheThingEvent: () => void;
Using version 1.x, in order to push an update to that subject, we have to do the following:
this.doTheThing = null;
This proposes an added builtin function, that can be called with no arguments and will next a new observable value to the subject. This function would be called as follows in version 2.x:
this.doTheThingEvent();
Add another Object.defineProperty()
for key + 'Event'
, for instances of @Observed({ type: 'subject' })
. The value would be defined as a function such as the following:
Object.defineProperty(target, key + 'Event', {
value: () => subject.next(null),
});
Observed
subjects. For instance, should this be applied to all types, or just type: 'subject'
, or additionally, type: replay'
but not type: 'behavior'
as that already has a fully fledged behavioral suite.key + 'Event'
definition: (value?: T) => subject.next(value ?? null)
this.myStringEvent('someString');
Since this includes no breaking changes, this could be a minor release.
In addition to accepting an ObservedDecoratorOptions
object argument, this would add support for accepting just a string of ObservedDecoratorType
// version 1.1.x syntax would still be supported:
@Observed({ type: 'subject' }) doTheThing: void;
// version 1.2.x syntax:
@Observed('subject') doTheThing: void;
Add an ObservedDecoratorType
type as follows, and use it as the type
in ObservedDecoratorOptions
:
export type ObservedDecoratorType = 'behavior' | 'subject' | 'replay';
export interface ObservedDecoratorOptions {
type: ObservedDecoratorType;
Change the options
argument on the @Observed
decorator to take either ObservedDecoratorType
or ObservedDecoratorOptions
:
export const Observed = (options: ObservedDecoratorType | ObservedDecoratorOptions = 'behavior') => {
With these decorators it becomes easy to create observable properties within a class.
But can the class itself be observable?
What that means is that if I subscribe to the class itself, I should get the whole entire class, all the properties of the class and any prototype methods. It'd likely be a BehaviourSubject since it would have a value as soon as the class is constructed.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.