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

在TypeScript中实现扩展元数据的ES6映射,typescript,dictionary,generics,Typescript,Dictionary,Generics,我需要一个映射,其中一个键不仅与值关联,还与一些元数据关联,比如“权重”或“timeToLive” 首先,一个示例界面,用于保存不同权重的项目: 导出接口{ 重量:数字 } …和类型别名,用于将所有元数据接口与要保存在映射中的实际值合并: 类型AugmValue=M&{ 值:V; } 我拿着内置的地图,把它扩展了。对于泛型类型: M表示元数据接口,如Weighted&Ttl K是一种钥匙 V是一种实际值 。。。和执行: 导出类MdaMap扩展映射{ 构造函数(记录:[K,V,M][]=[

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

首先,一个示例界面,用于保存不同权重的项目:

导出接口{
重量:数字
}
…和类型别名,用于将所有元数据接口与要保存在映射中的实际值合并:

类型AugmValue=M&{
值:V;
}
我拿着内置的地图,把它扩展了。对于泛型类型:

  • M表示元数据接口,如
    Weighted&Ttl
  • K是一种钥匙
  • V是一种实际值
。。。和执行:

导出类MdaMap扩展映射{
构造函数(记录:[K,V,M][]=[]{
常量结果:[K,AugmValue][]=[];
for(记录的常量[键、值、元]){
push([key,Object.assign({value:val},meta)]);
}
超级(结果);
}
get(key:K):V |未定义{
const t=super.get(键);
返回typeof t===“未定义”?未定义:t.value;
}
}
但是,
get
会在长消息下面加下划线,结尾为:

Type 'V' is not assignable to type 'AugmValue<V, M>'.
      Type 'V' is not assignable to type 'M'.
如果扩展
Map
,则需要实现扩展,这意味着
get(key:K)
需要返回
AugmValue>|未定义的
,而不是
V |未定义的

您可以使用的一种可能性是
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
这太痛苦了,我想知道你是否应该只使用
Map
,而不尝试创建一个新类



无论如何,希望这些想法中的一个能给你一些帮助。祝你好运

谢谢你详尽的回答。我考虑了两种方法。首先是扩展地图-正如您所想象的,这个解决方案是不受欢迎的,令人困惑的。所以第二个选择是围绕地图创建包装器-但正如您所注意到的,它很乏味而且。。。不愉快的不过,我还是希望有一个干净、无痛的界面,所以使用
Map
并不能让我满意。然而,我想知道重新实现Map是否不是一条可行之路。我打算询问关于SO的实施细节,我会看看的。再次感谢您的回答,这很有启发性
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";
}