TypeScript Proxy封装缓存对象赋值报类型错误问题
问题背景
我的应用内置了一套缓存机制,该缓存被Proxy对象包裹,用于实现缓存过期校验逻辑。
以下是Lists.d.ts文件中部分列表类型定义供参考,问题并非出在这部分代码中:
interface MetaData { Title: string | null; FileSystemObjectType: number; Id: number; // .... } // The CreationProps types just define the fields in the object type DeviceCreationProps = { Model: string; Manufacturer: string; // ... }; type CommentData = MetaData & CommentCreationProps; type DeviceData = MetaData & DeviceCreationProps; type UserData = MetaData & UserCreationProps; type ListDataMap = { Comments: CommentData; Devices: DeviceData; Users: UserData; }; type ListName = keyof ListDataMap;
ListItemCache.ts文件中的代码如下:
type CacheExpirationMapValue = { // [list name] : expiration date object [List in ListName]?: Date; }; const CacheExpirationMap: CacheExpirationMapValue = {}; type RawListItemCacheValue = { // [name of list]: array of list items [List in ListName]?: ListDataMap[List][]; }; const RawListItemCache: RawListItemCacheValue = {}; const maxAge = 1000 * 60 * 5; // This is a proxied object that allows us to handle the expirations when fetching cached objects const ListItemCache = new Proxy(RawListItemCache, { set<List extends ListName>(obj, prop: List, value: ListDataMap[List][]) { const expires = new Date(Date.now() + maxAge); CacheExpirationMap[prop] = expires; obj[prop] = value; return true; }, get(obj, prop) { const item = obj[prop]; const now = new Date(); if (item && !!CacheExpirationMap[prop] && CacheExpirationMap[prop] > now) { return item; } return []; }, }); export default ListItemCache;
该文件内代码无任何类型报错,但在其他文件中尝试为缓存赋值时,例如编写如下代码:
export default function usePopulateCache<List extends ListName>(list: List) { const { sp } = useContext(SPContext); useEffect(() => { sp.web.lists .getByTitle(list) .items<ListDataMap[List][]>() .then((items) => { ListItemCache[list] = items; }); }, []); }
赋值语句处出现类型报错,报错截图如下:
无法定位报错原因:用于索引两侧类型的List泛型完全一致,按照类型定义,RawListItemCacheValue[List]本应与ListDataMap[List][]类型完全匹配。
报错原因
报错核心是TypeScript对Proxy对象的默认类型推导没有保留自定义的泛型关联规则:
- 在
set捕获器上单独声明的<List extends ListName>泛型,仅在Proxy内部的handler逻辑中生效,TS不会把这个泛型规则推导到最终导出的ListItemCache变量类型上 - 外部调用
ListItemCache[list] = items时,TS识别到的ListItemCache是默认Proxy实例类型,它的索引赋值参数类型是所有可能value类型的联合,当用泛型List extends ListName做索引时,TS无法证明传入的items类型和对应key要求的value类型严格匹配,就会抛出类型不兼容错误。
修复方案
显式给ListItemCache标注类型,跳过TS对Proxy实例的默认推导,直接使用提前定义好的RawListItemCacheValue作为对外暴露的类型即可:
// 显式标注变量类型,外部访问时直接按照RawListItemCacheValue做类型校验 const ListItemCache: RawListItemCacheValue = new Proxy(RawListItemCache, { set(obj, prop: ListName, value) { const expires = new Date(Date.now() + maxAge); CacheExpirationMap[prop] = expires; // 此处类型强转是因为TS在handler内部无法追踪动态索引的类型关联,逻辑上是类型安全的 (obj as any)[prop] = value; return true; }, get(obj, prop: ListName) { const item = (obj as any)[prop]; const now = new Date(); if (item && !!CacheExpirationMap[prop] && CacheExpirationMap[prop] > now) { return item; } return []; }, });
修改后外部赋值语句的类型报错会直接消失,内部缓存过期校验逻辑不受任何影响。
内容的提问来源于stack exchange,提问作者Abir Taheer
相关产品推荐
相关产品推荐

