Если вы расширяете Map<K, AugmValue<V, M>>
, ваше расширение должно реализовать этого интерфейса , то есть get(key: K)
должно возвращать AugmValue<V, M>> | undefined
, а не V | undefined
.
Одна доступная вам возможность - перегрузить метод 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
), тогда вы не можете использовать 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";
}
Это такая боль, что мне интересно, стоит ли просто использовать a Map<K, AugmValue<V, M>>
вместо того, чтобы вообще пытаться создать новый класс?
В любом случае, надеюсь, что одна из этих идей поможет вам.Удачи!