网站搜索

typescript - Record 键的通用类型


我想创建一个类,将一些东西存储在对象中

class BusServiceFactory<T extends string = string> {
  private readonly store: Record<T, Map<string, (val?: string) => unknown>> = {};

  subscribe(uid: string, eventName: T, callback: (val?: string) => unknown): void {
    this.store[eventName] ??= new Map();
    this.store[eventName].set(uid, callback);
  }

  unsubscribe(uid: string): void {
    Object.values(this.store).forEach((handler) => handler.delete(uid));
  }
...
}

enum CustomEvent {
  EventA = 'evtA',
  EventB = 'evtB',
  EventC = 'evtC',
}

export const CustomBus = new BusServiceFactory<CustomEvent>();

游乐场链接

主要思想是为 store 的键使用通用类型,这样当我想要覆盖可能的键时,就可以传入一个枚举。

使用那段代码,我在分配 store 的那一行得到了 Type '{}' is not assignable to type 'Record unknown>>'。

我在 unsubscribe 方法中也遇到 'handler' 的类型是 'unknown',我真的不明白为什么

答案#1

问题在于,通过说 store 是一个 Record<T, Map</*...*/>>,你要求它必须拥有 T 的每个可能值的属性,但 {} 并没有任何属性。你可以使用 Partial 来解决这个问题,但我认为对于你的用例,使用 Map 更合适(而且它还解决了“handler 的类型为 unknown”的问题),见评论:

class BusServiceFactory<T extends string = string> {
    // Changed this to a `Map`
    private readonly store: Map<T, Map<string, (val?: string) => unknown>> = new Map();

    subscribe(uid: string, eventName: T, callback: (val?: string) => unknown): void {
        // Updated this because `this.store` is now a map
        let entry = this.store.get(eventName);
        if (!entry) {
            entry = new Map();
            this.store.set(eventName, entry);
        }
        entry.set(uid, callback);
    }

    unsubscribe(uid: string): void {
        // And this
        for (const entry of this.store.values()) {
            entry.delete(uid);
        }
    }
    // ...
}

游乐场链接

答案#2

export type ValidatorSchema=Record<T, { validations: string[] }>;