Используя необязательный знак + вместе с модификаторами типа, мы можем создавать более явные и читабельные объявления типов. Также можем использовать знак - (минус) для удаления необязательных объявлений из свойств ?.
Например: у нас есть 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 для всех свойств.
Модификаторы перебора типа полезны, если:
- есть интерфейс, который невозможно изменить напрямую (например, из библиотеки);
- есть интерфейс, который хотим продолжать использовать для некоторых целей, и создать его небольшую вариацию (с использованием модификаторов) для использования в других целях.
В обоих случаях модификаторы перебора типа “следуют” форме исходного интерфейса; даже если в будущем исходный интерфейс изменится/будет изменен они просто расширят его в соответствии с указанными правилами.