angular - Nu se poate scrie o metodă de extensie pentru Map<TKey, TValue[]> în TypeScript
Sunt un utilizator nou de Angular/TS și mă bucur să folosesc metodele de extensie. Funcționau bine pe tipurile standard, dar acum încerc să scriu una pentru Map unde V va fi un array, dar din păcate nu pare să funcționeze.
Ceea ce încerc să fac este ca metoda de extensie să inițializeze harta V ca un array și să adauge valoarea sau să adauge dacă este deja inițializată.
declare global {
interface Map<TKey, TValue[]>
{
SetInArray(key:TKey, value:TValue):boolean;
}
}
Map.prototype.SetInArray = function<TKey, TValue>(key:TKey, value:TValue):boolean {
let isNew:boolean = false;
if(this.has(key) == false){
this.set(key, []);
isNew = true;
}
let items:TValue[] = this.get(key);
items.push(value);
return isNew;
};
export {};
Vă rog să mă sfătuiți
Mulțumesc
Răspuns #1
Există câteva probleme (pe lângă faptul că înțelepciunea populară spune că nu ar trebui să extinzi prototipurile obiectelor native). Prima este că atunci când declari o interfață sau un tip generic, parametrul său de tip trebuie să fie un nume simplu, nu o funcție a unui alt tip. Deci nu poți avea
interface Foo<T, U[]> {} // error
Dacă necesități ca al doilea parametru de tip să fie un tablou, ar trebui să folosiți o constrângere generică:
interface Foo<T, U extends any[]> {} // okay
Următoarea problemă este că nu poți combina declarațiile de tipuri generice dacă modifici parametrii de tip sau constrângerile acestora într-un fel. Map<K, V> este deja definit în biblioteca standard, așa că nu îl poți schimba în Map<TKey, TValue[]> sau Map<TKey, TValue extends any[]> sau chiar Map<K, V extends any[]>. Trebuie doar să îl lași ca Map<K, V>.
Noua metodă pe care o adaugi necesită ca V să fie de tip array pentru a avea sens. Și nu poți restricționa V în declarația ta. Ești blocat? Nu! Din fericire, poți folosi o caracteristică TypeScript numită parametru this. Ideea este că poți adăuga un parametru numit this la începutul listei tale de parametri într-o funcție sau metodă. Când apelezi metoda, nu folosești parametrul this, dar TypeScript va impune ca metoda să fie apelată doar pe un obiect al cărui context this corespunde.
Așa:
// no error
declare global {
interface Map<K, V> {
SetInArray<V>(this: Map<K, V[]>, key: K, value: V): boolean;
}
}
Map.prototype.SetInArray = function <K, V>(this: Map<K, V[]>, key: K, value: V): boolean {
let isNew: boolean = false;
if (this.has(key) == false) {
this.set(key, []);
isNew = true;
}
let items: V[] = this.get(key)!;
items.push(value);
return isNew;
};
Hai să încercăm:
const numberArrayMap = new Map<string, number[]>();
numberArrayMap.set("a", [1, 2, 3])
numberArrayMap.SetInArray("a", 4); // okay
const numberMap = new Map<string, number>();
numberMap.set("a", 4)
numberMap.SetInArray("a", 4); // error
// The 'this' context of type 'Map<string, number>' is not assignable
// to method's 'this' of type 'Map<string, number[]>'.
// Type 'number' is not assignable to type 'number[]'.
Acesta este exact comportamentul pe care îl dorești, cred. Observă cum numberArrayMap îți permite să apelezi SetInArray(), dar numberMap nu, iar eroarea îți spune că, deși Map are într-adevăr o metodă SetInArray, pur și simplu nu o poți apela din cauza unui context this incompatibil.
Bine, sper că asta ajută. Mult noroc!
