@@ -26,24 +26,47 @@ export type LifecycleHookKey = keyof AllHooks;
2626type AllHookOptions = Record < LifecycleHookKey , true > ;
2727type DecorateHookOptions = Partial < AllHookOptions > ;
2828
29+ export interface TypedSimpleChange < Data > {
30+ previousValue : Data ;
31+ currentValue : Data ;
32+ firstChange : boolean ;
33+ }
34+
35+ /**
36+ * FIRST POINT:
37+ * the key is made optional because an ngOnChanges will only give keys of inputs that have changed
38+ * SECOND POINT:
39+ * the value is associated with `| null` as if an input value is defined but actually retrieved with
40+ * an `async` pipe, we'll initially get a `null` value
41+ *
42+ * For both point, feel free to check the following stackblitz that demo this
43+ * https://stackblitz.com/edit/stackblitz-starters-s5uphw?file=src%2Fmain.ts
44+ */
45+ export type TypedSimpleChanges < Component , Keys extends keyof Component > = {
46+ [ Key in Keys ] ?: TypedSimpleChange < Component [ Key ] > | null ;
47+ } ;
48+
2949// none of the hooks have arguments, EXCEPT ngOnChanges which we need to handle differently
30- export type DecoratedHooks = Record < Exclude < LifecycleHookKey , 'ngOnChanges' > , Observable < void > > & {
31- ngOnChanges : Observable < Parameters < OnChanges [ 'ngOnChanges' ] > [ 0 ] > ;
50+ export type DecoratedHooks < Component = any , Keys extends keyof Component = any > = Record <
51+ Exclude < LifecycleHookKey , 'ngOnChanges' > ,
52+ Observable < void >
53+ > & {
54+ ngOnChanges : Observable < TypedSimpleChanges < Component , Keys > > ;
3255} ;
3356export type DecoratedHooksSub = {
3457 [ k in keyof DecoratedHooks ] : DecoratedHooks [ k ] extends Observable < infer U > ? Subject < U > : never ;
3558} ;
3659
37- type PatchedComponentInstance < K extends LifecycleHookKey > = Pick < AllHooks , K > & {
38- [ hookSubject ] : Pick < DecoratedHooksSub , K > ;
60+ type PatchedComponentInstance < Hooks extends LifecycleHookKey = any > = Pick < AllHooks , Hooks > & {
61+ [ hookSubject ] : Pick < DecoratedHooksSub , Hooks > ;
3962 constructor : {
4063 prototype : {
41- [ hooksPatched ] : Pick < DecorateHookOptions , K > ;
64+ [ hooksPatched ] : Pick < DecorateHookOptions , Hooks > ;
4265 } ;
4366 } ;
4467} ;
4568
46- function getSubjectForHook ( componentInstance : PatchedComponentInstance < any > , hook : LifecycleHookKey ) : Subject < void > {
69+ function getSubjectForHook ( componentInstance : PatchedComponentInstance , hook : LifecycleHookKey ) : Subject < void > {
4770 if ( ! componentInstance [ hookSubject ] ) {
4871 componentInstance [ hookSubject ] = { } ;
4972 }
@@ -87,10 +110,12 @@ function getSubjectForHook(componentInstance: PatchedComponentInstance<any>, hoo
87110/**
88111 * Library authors should use this to create their own lifecycle-aware functionality
89112 */
90- export function getObservableLifecycle ( classInstance : any ) : DecoratedHooks {
91- return new Proxy ( { } as DecoratedHooks , {
92- get ( target : DecoratedHooks , p : LifecycleHookKey ) : Observable < void > {
93- return getSubjectForHook ( classInstance , p ) . asObservable ( ) ;
113+ export function getObservableLifecycle < Component , Inputs extends keyof Component = never > (
114+ classInstance : Component ,
115+ ) : DecoratedHooks < Component , Inputs > {
116+ return new Proxy ( { } as DecoratedHooks < Component , Inputs > , {
117+ get ( target : DecoratedHooks < Component , Inputs > , p : LifecycleHookKey ) : Observable < void > {
118+ return getSubjectForHook ( classInstance as unknown as PatchedComponentInstance , p ) . asObservable ( ) ;
94119 } ,
95120 } ) ;
96121}
0 commit comments