我需要一个映射,其中键不仅与值关联,而且还与某些元数据关联,例如“ weight”或“ timeToLive”。
首先,一个Map界面示例,其中包含不同权重的项目:
export interface Weighted {
weight: number
}
...以及用于将所有元数据接口与实际值合并以保存在Map中的类型别名:
type AugmValue<V, M> = M & {
value: V;
}
我拿了内置Map并将其扩展。从通用类型开始:
Weighted & Ttl
...和实现:
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];
}
答案 0 :(得分:1)
如果扩展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")
……但是它具有简洁且易于使用的巨大优势。正确键入。
如果确实需要制作某种与基类不兼容的派生类(如果get(key)
需要返回V | undefined
),则不能使用{{1} },因为您没有产生有效的子类型。在这种情况下,通常的建议是您的新类实例应具有基类的实例,而不是试图成为基类的实例。这称为composition over inheritance。从概念上讲这很清楚,但是在实践中却很烦人,因为它最终会将大多数方法转发到保留的实例:
extends
那真是太痛苦了,我不知道您是否应该使用一个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";
}
而不是尝试创建一个新的类?
无论如何,希望这些想法之一能为您提供帮助。祝你好运!