@@ -56,6 +56,14 @@ export type Projector<Selectors extends Observable<unknown>[], Result> = (
56
56
...args : SelectorResults < Selectors >
57
57
) => Result ;
58
58
59
+ type SignalsProjector < Signals extends Signal < unknown > [ ] , Result > = (
60
+ ...values : {
61
+ [ Key in keyof Signals ] : Signals [ Key ] extends Signal < infer Value >
62
+ ? Value
63
+ : never ;
64
+ }
65
+ ) => Result ;
66
+
59
67
@Injectable ( )
60
68
export class ComponentStore < T extends object > implements OnDestroy {
61
69
// Should be used only in ngOnDestroy.
@@ -67,10 +75,12 @@ export class ComponentStore<T extends object> implements OnDestroy {
67
75
private isInitialized = false ;
68
76
// Needs to be after destroy$ is declared because it's used in select.
69
77
readonly state$ : Observable < T > = this . select ( ( s ) => s ) ;
70
- private ɵhasProvider = false ;
71
-
72
78
// Signal of state$
73
- readonly state : Signal < T > ;
79
+ readonly state : Signal < T > = toSignal (
80
+ this . state$ . pipe ( takeUntil ( this . destroy$ ) ) ,
81
+ { requireSync : false , manualCleanup : true }
82
+ ) ;
83
+ private ɵhasProvider = false ;
74
84
75
85
constructor ( @Optional ( ) @Inject ( INITIAL_STATE_TOKEN ) defaultState ?: T ) {
76
86
// State can be initialized either through constructor or setState.
@@ -79,10 +89,6 @@ export class ComponentStore<T extends object> implements OnDestroy {
79
89
}
80
90
81
91
this . checkProviderForHooks ( ) ;
82
- this . state = toSignal ( this . stateSubject$ . pipe ( takeUntil ( this . destroy$ ) ) , {
83
- requireSync : false ,
84
- manualCleanup : true ,
85
- } ) ;
86
92
}
87
93
88
94
/** Completes all relevant Observable streams. */
@@ -293,12 +299,40 @@ export class ComponentStore<T extends object> implements OnDestroy {
293
299
}
294
300
295
301
/**
296
- * Returns a signal of the provided projector function.
297
- *
298
- * @param projector projector function
302
+ * Creates a signal from the provided state projector function.
303
+ */
304
+ selectSignal < Result > ( projector : ( state : T ) => Result ) : Signal < Result > ;
305
+ /**
306
+ * Creates a signal by combining provided signals.
299
307
*/
300
- selectSignal < K > ( projector : ( state : T ) => K ) : Signal < K > {
301
- return computed ( ( ) => projector ( this . state ( ) ) ) ;
308
+ selectSignal < Signals extends Signal < unknown > [ ] , Result > (
309
+ ...signalsWithProjector : [
310
+ ...selectors : Signals ,
311
+ projector : SignalsProjector < Signals , Result >
312
+ ]
313
+ ) : Signal < Result > ;
314
+ selectSignal (
315
+ ...args :
316
+ | [ ( state : T ) => unknown ]
317
+ | [
318
+ ...signals : Signal < unknown > [ ] ,
319
+ projector : ( ...values : unknown [ ] ) => unknown
320
+ ]
321
+ ) : Signal < unknown > {
322
+ if ( args . length === 1 ) {
323
+ const projector = args [ 0 ] as ( state : T ) => unknown ;
324
+ return computed ( ( ) => projector ( this . state ( ) ) ) ;
325
+ }
326
+
327
+ const signals = args . slice ( 0 , - 1 ) as Signal < unknown > [ ] ;
328
+ const projector = args [ args . length - 1 ] as (
329
+ ...values : unknown [ ]
330
+ ) => unknown ;
331
+
332
+ return computed ( ( ) => {
333
+ const values = signals . map ( ( signal ) => signal ( ) ) ;
334
+ return projector ( ...values ) ;
335
+ } ) ;
302
336
}
303
337
304
338
/**
0 commit comments