Невозможно написать метод расширения для картыв TypeScript - PullRequest
0 голосов
/ 29 ноября 2018

Я новый пользователь Angular / TS и наслаждаюсь использованием методов расширения.Они хорошо работали со стандартными типами, но сейчас я пытаюсь написать один для Map, где V будет массивом, но, к сожалению, он не работает.

То, что я пытаюсь сделать, - это то, что метод расширения будет либо инициализировать карту V как массив, и выдвинуть значение или нажать, если уже инициализирован.

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 {};

Пожалуйста, сообщите

Спасибо

1 Ответ

0 голосов
/ 29 ноября 2018

Есть несколько проблем (кроме того факта, что не следует расширять прототипы нативных объектов ).Во-первых, когда вы объявляете универсальный интерфейс или тип, его параметр типа должен быть пустым именем, а не функцией какого-либо другого типа.Таким образом, у вас не может быть

interface Foo<T, U[]> {} // error

Если вам требуется, чтобы параметр второго типа был массивом, вы должны использовать универсальное ограничение :

interface Foo<T, U extends any[]> {} // okay

Следующая проблема заключается в том, что вы не можете объединить объявления универсальных типов, если вы каким-либо образом измените параметры типа или их ограничения.Map<K, V> уже определен в стандартной библиотеке , поэтому вы не можете изменить его на Map<TKey, TValue[]> или Map<TKey, TValue extends any[]> или даже Map<K, V extends any[]>.Вы должны просто оставить его как Map<K, V>.

Новый метод, который вы добавляете, требует, чтобы V был типом массива, чтобы иметь смысл.И вы не можете ограничить V в своем объявлении.Вы застряли?Нет!К счастью, вы можете использовать функцию TypeScript под названием this параметр .Идея состоит в том, что вы можете добавить параметр с именем this в начало списка параметров функции или метода.Когда вы вызываете метод, вы не используете параметр this, но TypeScript заставит вас вызывать этот метод только для объекта, контекст которого this совпадает.

Вот так:

// 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;
};

Давайте попробуем:

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[]'.

Это именно то поведение, которое вы хотите, я думаю.Обратите внимание, что numberArrayMap позволяет вам вызывать SetInArray(), а numberMap - нет, и ошибка говорит вам, что, хотя Map имеет метод SetInArray, вы просто не можете вызвать его из-за несовместимого thiscontext.

Хорошо, надеюсь, это поможет.Удачи!

...