代码之家  ›  专栏  ›  技术社区  ›  Forseti

在TypeScript中实现用元数据扩充的ES6映射

  •  0
  • Forseti  · 技术社区  · 7 年前

    我需要一个映射,其中一个键不仅与值关联,还与一些元数据关联,比如“weight”或“timeToLive”。

    首先,一个示例接口,用于保存不同权重的项:

    export interface Weighted {
        weight: number
    }
    

    …和一个类型别名,用于将所有元数据接口与要保存在映射中的实际值合并:

    type AugmValue<V, M> = M & {
        value: V;
    }
    

    我把内置的地图扩展了。对于泛型类型:

    • M代表元数据接口,比如 Weighted & Ttl
    • K是一种钥匙

    ... 设计和实施:

    export class MdaMap<M, K, V> extends Map<K, AugmValue<V, M>> {
        constructor(records: [K, V, M][] = []) {
            const result: [K, AugmValue<V, M>][] = [];
            for (const [key, val, meta] of records) {
                result.push([key, Object.assign({value: val}, meta)]);
            }
            super(result);
        }
    
        get(key: K): V | undefined {
            const t = super.get(key);
            return typeof t === 'undefined' ? undefined : t.value;
        }
    }
    

    然而, get

    Type 'V' is not assignable to type 'AugmValue<V, M>'.
          Type 'V' is not assignable to type 'M'.
    

    如何正确实施?这是一个简单的例子,最终我想 get()

    get(key: K, meta: keyof AugmValue<V, M> = 'value'): AugmValue<V, M> [keyof M | "value"] | undefined {
        const t = super.get(key);
        return typeof t === 'undefined' ? undefined : t[meta];
    }
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   jcalz    7 年前

    如果你延伸 Map<K, AugmValue<V, M>> ,您的扩展需要实现 that interface get(key: K) 需要返回 AugmValue<V, M>> | undefined ,不是 V | undefined

    你有一种可能 overload 这个 get()

    export class MdaMap<M, K, V> extends Map<K, AugmValue<V, M>> {
    
      get(key: K): AugmValue<V, M> | undefined;
      get<AK extends keyof AugmValue<V, M>>(
        key: K, 
        augmentedValueKey: AK
      ): AugmValue<V, M>[AK] | undefined;
      get<AK extends keyof AugmValue<V, M>>(
        key: K, 
        augmentedValueKey?: AK
      ): AugmValue<V, M> | AugmValue<V, M>[AK] | undefined {
        const t = super.get(key);
        return typeof augmentedValueKey === 'undefined' ? t : 
          typeof t === 'undefined' ? undefined : t[augmentedValueKey];
      }
    
    }
    
    const mdaMap = new MdaMap([["a", true, { weight: 4 }], ["b", false, { weight: 17 }]]);
    const augmented = mdaMap.get("a"); // AugmValue<boolean, Weighted> | undefined
    const unaugmented = mdaMap.get("a", "value"); // boolean | undefined
    const weight = mdaMap.get("a", "weight"); // number | undefined
    

    get(key) 没有回复你所希望的,你需要打电话 get(key, "value") 相反。。。但它有一个巨大的优势,即简洁和正确的键入。


    需要返回一个 ),则不能使用 extends 基类的实例,而不是尝试 基类的实例。这叫做 composition over inheritance . 这在概念上是清楚的,但在实践中实现起来相当烦人,因为它最终主要是将方法转发到持有的实例:

    class OtherMap<M, K, V> {
      private innerMap: Map<K, AugmValue<V, M>> = new Map();
      constructor(records: [K, V, M][] = []) {
        this.innerMap = new Map(records.map(
          ([k, v, m]) => [k, Object.assign({ value: v }, m)] as [K, AugmValue<V, M>]
        ));
      }
      clear(): void {
        return this.innerMap.clear();
      }
      delete(key: K): boolean {
        return this.innerMap.delete(key);
      }
      forEach(callbackfn: (value: M & { value: V; }, key: K, map: OtherMap<M, K, V>) => void, thisArg?: any): void {
        return this.innerMap.forEach((v, k) => callbackfn(v, k, this), thisArg);
      }
      get(key: K): V | undefined {
        return (this.innerMap.get(key) || { value: undefined }).value;
      }
      has(key: K): boolean {
        return this.innerMap.has(key);
      }
      set(key: K, value: M & { value: V; }): this {
        this.innerMap.set(key, value);
        return this;
      }
      get size(): number {
        return this.innerMap.size;
      }
      [Symbol
        .iterator](): IterableIterator<[K, M & { value: V; }]> {
        return this.innerMap[Symbol.iterator]();
      }
      entries(): IterableIterator<[K, M & { value: V; }]> {
        return this.innerMap.entries();
      }
      keys(): IterableIterator<K> {
        return this.innerMap.keys();
      }
      values(): IterableIterator<M & { value: V; }> {
        return this.innerMap.values();
      }
      [Symbol.toStringTag]: "Map";
    }
    

    真是太痛苦了,我不知道你是否应该 地图<K、 AugmValue<五、 M>&燃气轮机; 而不是尝试创建一个新类?


    不管怎样,希望这些想法中的一个能给你一些帮助。祝你好运!

    推荐文章