In the process of incorporating a deep-watch property decorator in Angular, the following usage has been implemented:
@Component({ /* ... */ })
export class AppComp {
@Watch(
'a.b.c',
function (last, current, firstChange) {
// callback
}
)
prop = { a: { b: { c: 'Hello' } };
}
Most of the implementation is complete, however, the callback lacks specific types. The this
type is currently any
, and both last
and current
parameters are also defined as any
.
To provide these types to the callback function through generics, the following approach can be applied:
type PropType = { a: { b: { c: string } };
@Component({ /* ... */ })
export class AppComp {
// ↓ generic here
@Watch<AppComp, PropType>(
'a.b.c',
function (last, current, firstChange) {
// callback
// this -> AppComp
// last -> PropType
// current -> PropType
}
)
prop: PropType = { a: { b: { c: 'Hello' } };
}
While effective, this approach may seem verbose given that it's clear in this context that the callback function should have AppComp
for this
and PropType
for last
and current
parameters.
A simplified version of the Watch
definition is provided below:
interface IWatchDirectiveTarget {
ngDoCheck?: Function;
}
interface CallbackFunction<ThisType, WatchedType> {
(this: ThisType, last: WatchedType, current: WatchedType, firstChange: boolean): any;
}
export function Watch<ThisType, WatchedType> (
path: string,
cb: CallbackFunction<ThisType, WatchedType>
): (
target: IWatchDirectiveTarget,
key: string
) => void {
return function (
target: IWatchDirectiveTarget,
key: string
) {
target.ngDoCheck = function () {
// some change detecting logic...
cb.call(this, lastValue, currentValue, isFirst);
};
}
}
The challenge lies in passing types into Watch
and subsequently from Watch
into CallbackFunction
. There's a desire for a more streamlined approach using macros or similar concepts represented by placeholders like __CURRENT_CLASS_TYPE__
or __CURRENT_PROPERTY_TYPE__
:
@Watch<__CURRENT_CLASS_TYPE__, __CURRENT_PROPERTY_TYPE__>(path, cb)
Although not aesthetically pleasing, grouping
@Watch<__CURRENT_CLASS_TYPE__, __CURRENT_PROPERTY_TYPE__>
together could potentially simplify things and detach them from specific class and property names.
Alternatively, incorporating macro-like elements within the Watch
definition could eliminate the need for external generic type parameters:
function Watch (
path: string,
cb: CallbackFunction<__CURRENT_CLASS_TYPE__, __CURRENT_PROPERTY_TYPE__>
): (
target: IWatchDirectiveTarget,
key: string
) => void {
return function (
target: IWatchDirectiveTarget,
key: string
) {
target.ngDoCheck = function () {
// some change detecting logic...
cb.call(this, lastValue, currentValue, isFirst);
};
}
}
Is there a feasible way to achieve this level of abstraction?
Edit:
Reference has been made to: TypeScript property decorator that takes a parameter of the decorated property type
There seems to be potential for establishing a connection between the value provided to the decorator factory and the type of the decorated property...