@@ -4,11 +4,22 @@ type MergedStateValue<Obj extends Record<string, State>> = {
44 [ key in keyof Obj ] : [ key : key , value : Obj [ key ] [ "value" ] ] ;
55} [ keyof Obj ] ;
66
7- export class ReadonlyState < T = any > {
8- protected subscribers : Subscriber < T > [ ] = [ ] ;
7+ export interface ReadonlyState < T = any > {
8+ value : T ;
9+ listen ( listener : Subscriber < T > ) : void ;
10+ map < U > ( mapper : ( t : T ) => U ) : ReadonlyState < U > ;
11+ into ( state : State < T > ) : void ;
12+ }
13+
14+ const StateSymbol = Symbol ( "@hyperactive/state" ) ;
15+
16+ export class State < T = any > implements ReadonlyState < T > {
17+ #subscribers: Subscriber < T > [ ] = [ ] ;
918 #state: { value : T } ;
19+ [ StateSymbol ] = true ;
1020
11- constructor ( state : { value : T } ) {
21+ constructor ( value : T ) {
22+ const state = { value } ;
1223 this . #state = state ;
1324 }
1425
@@ -17,33 +28,7 @@ export class ReadonlyState<T = any> {
1728 }
1829
1930 static isState < X > ( x : X ) : x is Extract < X , State | ReadonlyState > {
20- return x instanceof ReadonlyState ;
21- }
22-
23- listen ( listener : Subscriber < T > ) {
24- this . subscribers . push ( listener ) ;
25- }
26-
27- map < U > ( mapper : ( t : T ) => U ) {
28- const s = new State ( mapper ( this . value ) ) ;
29- // publish mapped changes when value changes
30- this . listen ( value => s . publish ( mapper ( value ) ) ) ;
31- // return readonly so mapped state can't be published into
32- return s . readonly ( ) ;
33- }
34-
35- into ( state : State < T > ) {
36- this . listen ( value => state . publish ( value ) ) ;
37- }
38- }
39-
40- export class State < T = any > extends ReadonlyState < T > {
41- #state: { value : T } ;
42-
43- constructor ( value : T ) {
44- const state = { value } ;
45- super ( state ) ;
46- this . #state = state ;
31+ return x && typeof x === "object" && StateSymbol in x ;
4732 }
4833
4934 /**
@@ -55,31 +40,55 @@ export class State<T = any> extends ReadonlyState<T> {
5540
5641 static merge < T , RefMap extends { [ k : string ] : State } > (
5742 ...states : [ State < T > | RefMap , ...State < T > [ ] ]
58- ) : State < T > | State < [ number , T ] > | State < MergedStateValue < RefMap > > {
43+ ) : ReadonlyState < [ number , T ] > | ReadonlyState < MergedStateValue < RefMap > > {
5944 if ( State . isState ( states [ 0 ] ) ) {
6045 const merged = new State < [ number , T ] > ( [ 0 , states [ 0 ] . value ] ) ;
6146 for ( let index = 0 ; index < states . length ; index ++ ) {
6247 const state = states [ index ] as State < T > ;
6348 state . listen ( updated => merged . publish ( [ index , updated ] ) ) ;
6449 }
65- return merged ;
50+ return merged . readonly ( ) ;
6651 } else {
6752 const obj = states [ 0 ] ;
6853 type MergedValue = MergedStateValue < RefMap > ;
6954 const merged = new State < MergedValue > ( Object . values ( obj ) [ 0 ] ?. value ) ;
7055 for ( const key in obj ) obj [ key ] . listen ( updated => merged . publish ( [ key , updated ] ) ) ;
71- return merged ;
56+ return merged . readonly ( ) ;
7257 }
7358 }
7459
7560 publish ( next : T | Promise < T > ) {
7661 return Promise . resolve ( next ) . then ( val => {
7762 this . #state. value = val ;
78- this . subscribers . forEach ( subscriber => subscriber ( val ) ) ;
63+ this . # subscribers. forEach ( subscriber => subscriber ( val ) ) ;
7964 } ) ;
8065 }
8166
67+ listen ( listener : Subscriber < T > ) {
68+ this . #subscribers. push ( listener ) ;
69+ }
70+
71+ map < U > ( mapper : ( t : T ) => U ) : ReadonlyState < U > {
72+ const s = new State ( mapper ( this . value ) ) ;
73+ // publish mapped changes when value changes
74+ this . listen ( value => s . publish ( mapper ( value ) ) ) ;
75+ // return readonly so mapped state can't be published into
76+ return s . readonly ( ) ;
77+ }
78+
79+ into ( state : State < T > ) {
80+ this . listen ( value => state . publish ( value ) ) ;
81+ }
82+
8283 readonly ( ) {
83- return new ReadonlyState ( this . #state) ;
84+ return {
85+ get value ( ) : T {
86+ return this . value ;
87+ } ,
88+ listen : this . listen . bind ( this ) ,
89+ map : this . map . bind ( this ) ,
90+ into : this . into . bind ( this ) ,
91+ [ StateSymbol ] : true ,
92+ } ;
8493 }
8594}
0 commit comments