我希望能够编写一个类型安全的Memoize函数,该函数支持采用泛型函数,并支持通过返回函数的赋值属性清除内部缓存,如下所示:
const memoizeSafeButLosesGenerics = <Args extends unknown[], RV>(fn: (...args: Args) => RV) => {
const cache = new Map<string, RV>();
const memoed = (...args: Args): RV => {
// implementation here...
return fn(...args);
};
memoed.clear = () => cache.clear();
return memoed;
};
不幸的是,当我测试这一点时,TypeScrip不能保留所提供的fn
:
const test1 = memoizeSafeButLosesGenerics(<T,>(t: T) => t);
// test1 is:
// {
// (t: unknown): unknown;
// clear(): void;
// }
值得注意的是,删除倒数第二行(其中赋值memoed.clear
)does将保留泛型类型参数,因此我认为赋值的某些方面会改变memoed
的类型,因为它与其推断相关.
有一种解决方法可以保留泛型类型参数,但是它需要类型断言,并允许人们在Memoize函数的实现中做不安全的事情.例如:
const memoizeUnsafePreservesGenericsWorkaround = <Fn extends (...args: unknown[]) => unknown>(fn: Fn) => {
type RV = ReturnType<Fn>;
const cache = new Map<string, RV>();
type UnsafeExampleArgs = Parameters<Fn> | [] | null[];
const memoed = ((...args: UnsafeExampleArgs): RV => {
// implementation here...
// I would expect UnsafeExampleArgs to _not_ be valid arguments to fn
return fn(...args); // I would expect calling fn _would_ be a valid RV
}) as Fn;
const memoedClearable = Object.assign(memoed, {
clear: () => cache.clear()
});
return memoedClearable;
};
const test2 = memoizeUnsafePreservesGenericsWorkaround(<T,>(t: T) => t);
// test2 is ✅:
// (<T>(t: T) => T) & {
// clear: () => void;
// }
有没有第三种方法来编写Memoize,在这种方式下,提供的函数的泛型类型参数被保留,在Memoize的实现中是否not涉及类型断言?