Антон Ларичев
Сегодня разберём, что же нового добавили в TypeScript 4.9
и чем это будет для нас полезно.
Оператор satisfies
Начнём с оператора satisfies
, который поможет нам улучшить типизацию наших объектов. При этом не ограничивая нас, если объекты создали мы сами.
Пример:
Сделаем обычную типизацию ошибки:
// Тип ErrorData
type ErrorData = {
errorOrCode: number | string; // код ошибки
payload?: Record<string, string | number | boolean>; //payload (опциональный)
};
// Пример errorData
const errorData: ErrorData = {
errorOrCode: 'TIMEOUTERROR',
payload: {
userId: 1,
},
};
У нас есть некоторые данные ошибки errorData
и их тип ErrorData
:
errorOrCode
- код ошибки, который будет
number
либоstring
. Может прийти как 404 либо как TIMEOUTERROR.
- код ошибки, который будет
payload
- является опциональным. Содержит внутри объект, где ключом будет
string
, а значениями:string
либоnumber
, либоboolean
.
- является опциональным. Содержит внутри объект, где ключом будет
Создаем объект ошибки, чтобы потом его передать, но это не JS поэтому нам надо типизировать errorData
. Изначально мы все написали корректно. Теперь специально сделаем опечатку в слове payload
:
// тип ErrorData
type ErrorData = {
errorOrCode: number | string; // код ошибки
payload?: Record<string, string | number | boolean>; //payload (опциональный)
};
// данные errorData
const errorData: ErrorData = {
errorOrCode: 'TIMEOUTERROR',
// Ошибка!!! TypeScript пишет, что "paload" не существует в типе "ErrorData": "возможно вы хотели записать "payload"?"
paload: {
userId: 1,
},
};
Казалось бы теперь все хорошо и мы двигаемся дальше. Давайте попробуем преобразовать данные ошибки в нижний регистр:
// тип ErrorData
type ErrorData = {
errorOrCode: number | string; // код ошибки
payload?: Record<string, string | number | boolean>; //payload (опциональный)
};
// данные errorData
const errorData: ErrorData = {
errorOrCode: 'TIMEOUTERROR',
payload: {
userId: 1,
},
};
// пытаемся перевести в нижний регистр
errorData.errorOrCode. // отсутсвует метод toLowerCase()!
Видим, что отсутсвует метод toLowerCase()
. Всё потому, что errorOrCode
можеть быть как string
так и как number
. Поэтому мы не можем просто так написать toLowerCase()
. для этого нужно сделать проверку:
if (typeof errorData.errorOrCode == 'string') {
errorData.errorOrCode.toLowerCase();
}
Ещё хуже обстоят дела с userId
:
// и здесь пишет, что нет никакого 'userId' и 'payload' является опциональным
errorData.errorOrCode.userId;
Поэтому мы тут практически начинаем бороться с типизацией. Вот именно тогда на помощь к нам приходит satisfies
.
Вместо того чтобы говорить, что объект errorData
точно будет типа 'ErrorData', мы можем применить оператор satisfies
. Мы говорим, что объект может быть любым, но главное, чтобы он удовлетворял интерфейсу или в данном случае типу ErrorData
:
satisfies ErrorData;
Теперь можно убрать всякие условия и всё будет работать:
// тип ErrorData
type ErrorData = {
errorOrCode: number | string; // код ошибки
payload?: Record<string, string | number | boolean>; //payload (опциональный)
};
// данные errorData
const errorData = {
errorOrCode: 'TIMEOUTERROR',
// Ошибка!!! TypeScript пишет, что "paload" не существует в типе "ErrorData": "возможно вы хотели записать "payload"?"
paload: {
userId: 1,
},
} satisfies ErrorData; // применяем оператор `satisfies`
// всё работает
errorData.errorOrCode.toLowerCase();
errorData.errorOrCode.userId;
Оператор in
Он позволяет нам отделить один интерфейс от другого:
type A = {
a: 1;
b: 2;
};
type B = {
a: 1;
c: 3;
};
type C = A | B;
function myf(c: C) {
if ('c' in c) {
c; // принимает значение 'B'
}
}
И здесь c
принимает значение B, потому что мы отделили их по свойству. Благодаря этому мы можем легко сузить наши типы в рамках работы с функцией.
Однако есть ситуации, когда in
нам не поможет:
type externalData = unknown; // тип, который приходит извне
// передается 'data' типа 'externalData'
function getExternalData(data: externalData) {
// делаем проверку
if (typeof data == 'object' && !!data && 'userId' in data) {
// обращаемся к свойству
data.userId; // Ошибка!!! Свойство "userId" не существует в типе "object"
}
}
Хотя проверили, что это object
, не null
и что это свойство есть. Однако ошибка все равно возникает. А в версии TypeScript 4.9:
type externalData = unknown;
function getExternalData(data: externalData) {
if (typeof data == 'object' && !!data && 'userId' in data) {
data.userId // ошибки нет
}
Все отлично, ошибок нет.
Сравнение с NaN
Часто можно встретить как ошибку на код-ревью:
let a = 10;
// делаем проверку
if (a !== Nan) {
// TypeScript теперь подсвечивает выражение: 'Вы хотели использовать "!Number.isNaN(a)"?'
}
И теперь не будет таких простых ошибок с работой с NaN
, так как TypeScript теперь нам сообщает, что мы тут неправы.
Карта развития разработчика
Получите полную карту развития разработчика по всем направлениям: frontend, backend, devops, mobile
Комментарии
0