Используя необязательный знак +
вместе с модификаторами типа, мы можем создавать более явные и читабельные объявления типов. Также можем использовать знак -
(минус) для удаления необязательных объявлений из свойств ?
.
Например: у нас есть interface
; можем использовать модификаторы перебора типов, чтобы сделать все его свойства доступными только для чтения readonly
.
interface ICar {
name: string;
age: number;
}
type ReadonlyCar = {
readonly [K in keyof ICar]: ICar[K];
};
Подобный тип может быть полезен, например, для state
приложения Redux, потому что state
должно быть неизменным.
Мы не должны быть в состоянии изменить любое из его свойств, как только объект был создан. Модификаторы перебора типов стали отличным дополнением к языку, поскольку они позволяют легко расширять существующие типы и применять массовые изменения ко всем их свойствам.
Теперь, если мы объявим две модели машины car
: первый объект изменчив, другой - только для чтения; затем попробуем изменить их данные, то заметим, что во втором случае у нас будет ошибка.
const car: ICar = {
name: "Mercedes",
age: 2
};
const readOnlyCar: ReadonlyCar = {
name: "BMW",
age: 5
};
car.age = 8;
readOnlyCar.age = 10; // Cannot assign to 'age' because it is a read-only property
В случае с readOnlyCar.age
, TypeScript говорит нам, что age
только для чтения - Cannot assign to 'age' because it is a read-only property
.
И это нормально, ведь мы указали, что все его свойства только для чтения. Статус readonly
(только для чтения) - это не единственное, что мы можем изменить в модификаторах перебора типов.
Мы можем указать, что все свойства не обязательны через ?
.
type ReadonlyCar = {
readonly [K in keyof ICar]?: ICar[K];
};
Также, можем указать что все свойства - строки, или сделать каждое свойство как объединение их исходного типа и строки через вертикальную черту |
. Вариантов много.
type ReadonlyCar = {
readonly [K in keyof ICar]?: ICar[K] | string;
};
Однако, с синтаксисом readonly [K in keyof ICar]: ICar[K];
мы можем только добавлять новые элементы в существующие типы. Можем добавить флаг readonly
, или ?
.
Если у оригинального типа есть свойство которое необязательно, например:
interface ICar {
name: string;
age: number;
color?: string;
}
Мы можем убрать флаг необязательно - ?
. Начиная с TypeScript 2.8, стало возможным добавлять знак минус -
перед символом, который хотим удалить.
type ReadonlyCar = {
readonly [K in keyof ICar]-?: ICar[K];
};
Как только мы добавили знак минус, TypeScript тут же начал выдавать ошибку в const readOnlyCar
. Это потому что мы внезапно пропустили обязательное свойство в этом новом объекте. Как только добавим новое поле color
, ошибка исчезнет.
const readOnlyCar: ReadonlyCar = {
name: "BMW",
age: 5,
color: "black"
};
Поскольку у нас есть гибкость со знаком -
, чтобы удалять флаги из наших типов, знак +
также был добавлен к этой функции. Мы можем более четко сказать, что именно добавляем и что удаляем.
type ReadonlyCar = {
+readonly [K in keyof ICar]-?: ICar[K];
};
Теперь другим разработчикам, читающим этот тип, стало понятнее, что мы берем оригинальный интерфейс ICar
, удаляем все необязательные модификаторы -?
и добавляем флаг +readonly
для всех свойств.
Модификаторы перебора типа полезны, если:
- есть интерфейс, который невозможно изменить напрямую (например, из библиотеки);
- есть интерфейс, который хотим продолжать использовать для некоторых целей, и создать его небольшую вариацию (с использованием модификаторов) для использования в других целях.
В обоих случаях модификаторы перебора типа “следуют” форме исходного интерфейса; даже если в будущем исходный интерфейс изменится/будет изменен они просто расширят его в соответствии с указанными правилами.