Как написать «K расширяет ключ T» в общем классе и использовать K тогда - PullRequest
0 голосов
/ 29 апреля 2019

Я хочу создать класс, который получает объект в качестве параметра типа, а затем использует тип в качестве типа свойства в этом классе, чтобы я мог устанавливать и получать значения, только если этот ключ существует в этом объекте.

Я создаю интерфейс, назовем его IStorage.Класс Storage должен реализовывать iStorage и хранить значения в зависимости от того, существует ли ключ в данном объекте.

interface iStorage<T, K extends keyof T> {
    getField(k: K): T[K];

    setField(k: K, v: string | number | object): T[K];

    render(): void;
}

class Storage<T, K extends keyof T> implements iStorage<T, K> {
    private fields: { [key: K]: string | number | object };

    public getField(k: K): T[k] {
        return this.fields[k];
    }

    public setField(k: K, v: string | number | object): T[k] {
        return (this.fields[k] = v);
    }
}


let storage = new Storage<{name: string, type: number}>();

Я хочу, чтобы поля в Storage соответствовали общему определению (я думаю, T [K]), номашинописный текст говорит, что ключ должен иметь тип string или number, поэтому он не работает должным образом.В своем интерфейсе я бы хотел сказать, что вторым параметром setFormField также является T [K].Я также думаю, что мое определение типа повторяется.Typescript хочет, чтобы я также приводил аргумент секунд, что имеет смысл для меня, но я не хочу писать, что K расширяет keyof T каждый раз для функции, и при этом я не могу использовать его как тип свойства.

Много пробовал тихо, но не смог заставить его работать .. Я немного растерялся, так как пытаюсь это уже несколько часов.Никогда раньше не имел дела с дженериками, поэтому, пожалуйста, будьте осторожны со мной.: D

Надеюсь, кто-нибудь может мне помочь.

Ответы [ 2 ]

1 голос
/ 29 апреля 2019

Я не уверен на 100% в вашем случае использования, но я бы склонен изменить ваш код примерно так:

interface IStorage<T extends object> {
  getField<K extends keyof T>(k: K): T[K];
  setField<K extends keyof T>(k: K, v: T[K]): T[K];
  render(): void;
}

class Storage<T extends object> implements IStorage<T> {
  constructor(private fields: T) { }
  render() { }

  public getField<K extends keyof T>(k: K) {
    return this.fields[k];
  }

  public setField<K extends keyof T>(k: K, v: T[K]): T[K] {
    return (this.fields[k] = v);
  }
}

let storage = new Storage({ name: "str", type: 123 });
const str = storage.getField("name");
const num = storage.setField("type", 456);

Ссылка на игровую площадку

Здесь интерфейс IStorage зависит только от типа T объекта, а отдельные методы getField() и setField() сами по себе являются общими функциями, которые могут воздействовать на любой ключ свойства K из keyof T.,Параметр универсального типа K не отображается в самом интерфейсе.

Я изменил реализацию Storage для соответствия.Обратите внимание, что я заставил конструктор принять значение типа T, которое хранится как закрытый член.Если вы не хотите передавать начальное значение, вам придется ограничить T типом со всеми необязательными свойствами (то есть использовать что-то вроде Partial<T> вместо просто T.)

И из приведенного выше кода (или из ссылки на игровую площадку) видно, что IntelliSense - это то, что вам нужно ... известно, что вызов getField("name") возвращает значение типа string.

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

0 голосов
/ 29 апреля 2019

Ваш тип ключа [key: K] не может быть обработан Typescript.Какими бы были два объекта в качестве ключей?Когда они считаются равными?Следовательно, тип записей / словарных ключей должен быть либо строкой, либо числом, либо символом и ничем иным.

Вы могли бы иметь возможность имитировать то, что вы пытаетесьиспользуя массив кортежей, например Array<[KeyType, ValueType]>, и фильтруйте по первому элементу кортежей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...