/
Программирование
/
Курс Typescript
/

Динамические ключи (Index Signature)

Typescript: Динамические ключи (Index Signature)

В JavaScript при создании объектов мы можем использовать разные значения в качестве ключей. Среди значений могут быть строки, числа или символы. Ровно такие же возможности TypeScript предоставляет для своих объектных типов, с которыми мы научимся работать в этом уроке.

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

type dynamicKeysObject = {
  [key: string | number | symbol]: unknown;
};

Здесь мы объявили объектный тип dynamicKeysObject, в котором ключом может служить любой тип из доступных типов данных key: string | number | symbol. Попробуем указать такой тип для переменной:

const obj: dynamicKeysObject = {
  name: "John",
  age: 30,
  0: "zero",
  [Symbol("secret")]: "symbol",
};

Еще динамические ключи можно использовать совместно с указанными явно полями. Тогда ограничения динамических полей также будут распространяться и на них.

В примере ниже нам понадобится оператор satisfies. Он проверяет, что значение соответствует типу, но не подменяет им тип, который TypeScript вывел сам. Два знакомых нам способа так не умеют. Аннотация вида const theme: MyTheme = { ... } подменяет выведенный тип объявленным: поле palette.primary стало бы 'red' | 'green' | 'blue', а spacingunknown. Приведение as MyTheme подменяет тип точно так же, только вместо полноценной проверки верит нам на слово. А satisfies и проверку выполняет, и сохраняет точные выведенные типы: palette.primary остается 'red', а spacing.small — числом:

type MyTheme = {
  palette: {
    primary: "red" | "green" | "blue";
    [key: string]: string;
  };
  [key: string]: unknown;
};

const theme = {
  palette: {
    primary: "red",
  },
  spacing: {
    small: 8,
  },
} satisfies MyTheme;

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

Точно такой же синтаксис и поведение у динамических ключей в интерфейсах:

interface MyTheme {
  palette: {
    primary: string;
  };
  [key: string]: unknown;
}

В классах динамические ключи можно использовать для обычных и для static полей:

class Template {
  static [propName: string]: string | number;

  [key: string]: string;
}

Template.test = "test";

const template = new Template();
template.test = "test";

Template String Literal

Динамические ключи полезны там, где нам неизвестны все возможные имена полей объекта, но мы все равно хотим ограничить их тип. В TypeScript тип ключа может также быть и шаблонным литералом. Для примера попробуем объявить тип слушателя и потребовать, чтобы все его методы начинались со слова on:

type Listeners = {
  [key: `on${string}`]: (value: unknown) => void;
};

const streamListeners: Listeners = {
  onStart() {},
  onFinished() {},
};

Литеральный тип on${string} нам говорит, что мы ожидаем строку по шаблону «строка начинается с on, а дальше идет любая строка». Такая техника называется Template String Literal, она помогает наложить ограничения при типизации строк.

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

Задание

Реализуйте интерфейс EmployeeSalary, где ключом выступает имя (string), а значением — зарплата (number). Также реализуйте функцию buildSalaryStatistics(employees: EmployeeSalary): SalaryStatistics, которая должна возвращать минимальную (поле min), среднюю (поле avg) и самую высокую (поле max) зарплату.

const employees: EmployeeSalary = {
  mango: 100,
  poly: 50,
  ajax: 150,
};

employees.ironMan = 1000;

buildSalaryStatistics(employees); // { min: 50, max: 1000, avg: 325 }

Полезное

Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются