我尝试在angular中实现一个deep-watch属性装饰器,用法如下:
@Component({ /* ... */ })
export class AppComp {
@Watch(
'a.b.c',
function (last, current, firstChange) {
// callback
}
)
prop = { a: { b: { c: 'Hello' } } };
}
字符串
我几乎完成了,除了回调是无类型的,它的this
是any
,它的last
和current
参数也是any
。
使用泛型,我可以向回调函数提供这些类型:
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' } } };
}
型
但我认为这种模式是冗长的,因为在这种情况下很明显,回调函数中this
的类型应该是AppComp
,last
和current
参数的类型应该是PropType
。
下面是Watch
定义的简化版本:
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);
};
}
}
型
如您所见,我需要将类型传递到Watch
,并再次将这些类型从Watch
传递到CallbackFunction
。
我想要的是像macro这样的东西(举个例子,任何可以达到目的的东西都可以,macro只是我能想到的最简单的东西),比如__CURRENT_CLASS_TYPE__
或__CURRENT_PROPERTY_TYPE__
,我可以这样使用它们:
@Watch<__CURRENT_CLASS_TYPE__, __CURRENT_PROPERTY_TYPE__>(path, cb)
型
虽然看起来很丑,但至少@Watch<__CURRENT_CLASS_TYPE__, __CURRENT_PROPERTY_TYPE__>
可以被视为一个整体,并与类名和属性名解耦。
或者更好的是,在Watch
的定义中添加一些类似宏的东西,这样就可以在没有外部泛型类型参数的情况下重写它:
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);
};
}
}
型
这样的事情有可能做到吗?
编辑:
我读过这个:TypeScript property decorator that takes a parameter of the decorated property type
在提供给装饰器工厂的值的类型和装饰属性的类型之间建立某种关系似乎是可能的...
1条答案
按热度按时间tzdcorbm1#
我试着用**“一半”来回答我的问题。沿着post TypeScript property decorator that takes a parameter of the decorated property type的路径,我设法通过将其定义从decorator factory更改为decorator factory**来从用法中删除目标类型:
定义:
字符串
使用方法:
型
我说“一半”是因为我仍然需要指定值类型,但我认为这是可以接受的,因为我使用字符串字面量来指定值的路径,我不认为TS有能力从字符串字面量推断类型。
1.自动属性类型推断+值?.a?.B?.c
1.手动类型+值
很难说哪个更好。
这个解决方案并不完美,因为在使用中的双重调用对这个装饰器的用户来说是没有意义的,所以我不打算接受我自己的答案。我希望有更好的理解typescript的人可以想出更好的答案(单调用),甚至可以自动推断属性类型的解决方案。