Расширение интерфейса с подписью индекса в общем, имея типизацию для реализаций - PullRequest
0 голосов
/ 12 апреля 2019

У меня есть система, которая берет пары ключ: объект, а затем позволяет получить их позже в приложении.

Проблема в том, что эта подпись индекса прерывает ввод, потому что она позволяет любому ключу вообще существовать, поэтому вы не можете набирать запросы get ().

Ниже приведен краткий фрагмент приложения и проблемы.

Я попытался удалить эту подпись индекса, что является проблемой, потому что тогда setting[key].value в class Configuration не существует. Я попытался удалить базовый интерфейс настроек, но это в основном нарушает все ваши изменения в наборе текста. Я пробовал довольно много вспомогательных функций и средств защиты, чтобы заставить приложение понять, что это настройка. Я не нахожусь в той точке, где мне не придется принимать ввод с помощью метода get.

class Setting {
  constructor(
    public value: number,
    private locked: boolean
  ) { }
}

class Settings {
  [key: string]: Setting; // Comment me out: errors (but typing), leave me in: no errors (no typing)
}

interface SettingsCountryRank extends Settings {
  taiwan: Setting;
  china: Setting;
}

class Configuration<TSettings extends Settings> {
  settings: TSettings;

  public get<U extends keyof TSettings>(key: U): TSettings[U]['value'] {
    return this.settings[key].value;
  }

  constructor(settings: TSettings) {
    this.settings = settings;
  }
}

function main(){
  const test = new Configuration<SettingsCountryRank>({ taiwan: new Setting(1, true), china: new Setting(2, false) });

  test.get('china')
  test.get('notathing');
}

Когда вы включаете и выключаете линию в Settings, вы видите проблему.

1 Ответ

2 голосов
/ 13 апреля 2019

Я думаю, что вместо использования подписи индекса вы должны использовать сопоставленный тип , ключи которого ограничены известными значениями. Я буду использовать Record<K, V> сопоставленный тип как , определенный в стандартной библиотеке .

Важным битом является использование рекурсивных ограничений вида T extends Record<keyof T, Setting>, который гарантирует, что все свойства T должны иметь тип Setting, но который не накладывает никаких ограничений на ключи (подпись индекса говорит ограничивает ключи «всеми возможными строковыми значениями», а это не то, что вам нужно):

// Settings<T> has the same keys as T, and the values are all Setting
type Settings<T> = Record<keyof T, Setting>;

// constraining SettingsCountryRank to Settings<SettingsCountryRank> is
// makes sure that every declared property has type Setting:
interface SettingsCountryRank extends Settings<SettingsCountryRank> {
    taiwan: Setting;
    china: Setting;
    // oopsie: string; // uncomment this to see an error
}

// constraining TSettings to Settings<TSettings> lets the compiler know that
// all properties of TSettings are of type Setting
class Configuration<TSettings extends Settings<TSettings>> {
    settings: TSettings;

    public get<U extends keyof TSettings>(key: U): TSettings[U]['value'] {
        return this.settings[key].value;
    }

    constructor(settings: TSettings) {
        this.settings = settings;
    }
}

function main() {
    const test = new Configuration<SettingsCountryRank>({ taiwan: new Setting(1, true), china: new Setting(2, false) });
    test.get('china')
    test.get('notathing'); // error as expected
}

Это работает, как вы ожидаете, я думаю. Надеюсь, это поможет; удачи!

...