TypeScript - Как индексировать класс по строке без потери безопасности класса? - PullRequest
0 голосов
/ 30 апреля 2020

Рассмотрим следующий фрагмент кода TypeScript, который компилируется и успешно работает:

class UserSettings {
  [key: string]: boolean;

  setting1: boolean = false;
  setting2: boolean = false;
  setting3: boolean = false;
  otherOtherSetting: boolean = true;
}

const settings = new UserSettings();

const checkSettingChanged = (setting: string, value: boolean) => {
  if (value !== settings[setting]) {
    console.log(`The setting of ${setting} changed, updating it!`);
    settings[setting] = value;
  }
};

Однако этот код отстой, потому что, указав [key: string]: boolean в классе, мы теряем класс безопасности. Другими словами, следующий код успешно скомпилируется, но выдаст ошибку времени выполнения:

function someOtherFunction() {
  // Oops, I forgot to specify setting 4 in the class definition above,
  // so this code should fail to compile, but TypeScript doesn't care,
  // and now I will get a stupid runtime error
  if (settings.setting4) {
    console.log('Setting 4 is enabled!');
  }
}

ОК. Так можно ли удалить [key: string]: boolean в определении класса? Давайте попробуем и посмотрим, что произойдет:

class UserSettings {
  // This is just a "normal" TypeScript class now
  setting1: boolean = false;
  setting2: boolean = false;
  setting3: boolean = false;
  otherOtherSetting: boolean = true;
}

const settings = new UserSettings();

const checkSettingChanged = (setting: string, value: boolean) => {
  // Hopefully, adding this runtime check should satisfy the TypeScript compiler
  if (!(setting in settings)) {
    throw new Error(`The setting of ${setting} does not exist in the UserSettings class.`);
  }

  if (value !== settings[setting]) {
    console.log(`The setting of ${setting} changed, updating it!`);
    settings[setting] = value;
  }
};

Этот код не удовлетворяет компилятору TypeScript. Сбой со стороны value !== settings[setting], выдающий ошибку:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'UserSettings'.
  No index signature with a parameter of type 'string' was found on type 'UserSettings'.

Что дает? Как я могу построить проверку во время выполнения, которая удовлетворяет компилятору TypeScript?

1 Ответ

1 голос
/ 30 апреля 2020

In не будет работать как охрана типов, чтобы утверждать, что строка является ключом типа. Вы можете создать специальный тип защиты, который будет выполнять сужение:

class UserSettings {
  // This is just a "normal" TypeScript class now
  setting1: boolean = false;
  setting2: boolean = false;
  setting3: boolean = false;
  otherOtherSetting: boolean = true;
}

const settings = new UserSettings();
function isKeyOf<T>(p: PropertyKey, target: T): p is keyof T {
    return p in target;
}

const checkSettingChanged = (setting: string, value: boolean) => {
  // Hopefully, adding this runtime check should satisfy the TypeScript compiler
  if (!isKeyOf(setting, settings)) {
    throw new Error(`The setting of ${setting} does not exist in the UserSettings class.`);
  }

  if (value !== settings[setting]) {
    console.log(`The setting of ${setting} changed, updating it!`);
    settings[setting] = value;
  }
};

[Playground Link] ( Playground Link )

Однако это не полный тип безопасно. Basi c OOP диктует, что вы должны иметь в settings экземпляр класса, производного от UserSettings, который может иметь не все логические значения. Вы можете прочитать больше об этой проблеме здесь , поскольку она применима к Object.keys, но те же рассуждения верны здесь. Хотя в общем случае это небезопасно, вы все равно можете использовать его с указанным предупреждением.

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