Пейджер

🌍 Привет мир! 👋

🌍 Привет мир! 👋

Думаю многие из тех, кто пишет на TypeScript, делают “override” типа, при помощи type assertions.

type User = {
    userId: number;
    name: string;
}
const user = {} as User;

Да да да, это плохо, вы переопределяете тип, тем самым говорите тайпскрипту что вам виднее какая здесь типизация. И это опасно, в дальнейшем вы можете забыть добавить данные или добавить, но не все, как итог стреляете себе в ногу.

Есть случаи когда это действительно удобно:

📌 Совместимость со сторонними библиотеками: При работе с чужими библиотеками, там могут отсутствовать полные типы или методы только частично типизированны.
📌 Устранение ошибок компиляции: Когда TypeScript не может автоматически определить тип.
📌 Улучшение читаемости кода: За счёт явного указания типов, код может стать более читаемым и понятным для других разработчиков, указывая на намерения и ожидаемые данные.
📌 Работа с DOM: Часто используется для работы с объектами DOM, поскольку TypeScript может не знать конкретного типа элемента (например, HTMLInputElement, HTMLElement).

Но что если я вам скажу, что чаще использую Double assertion. Уверен, что не все применяют это на практике.

📚 Double assertion

Из описания выше понятно, что используя type assertions переопределяется тип, который вывел TypeScript, но иногда этого недостаточно. К примеру мы пишем тест для функции у которой внутри определенная реакция на невалидные данные.

type User = {
  id: number;
  name: string;
  email: string;
  age: number;
  isActive: boolean;
}

const processUser = (user: User) => {
  if (typeof user.isActive !== "boolean") {
    return "Что вы мне подснули?!"
  }
  return user
}

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

Можно написать просто "as any" и привести общий тип к любому значению, но скорее всего у вас в проекте действует правило no-explicit-any. И к тому же, это плохая практика, кто-то будет читать ваш код, и благодаря хотя бы осмысленному утверждению типа “as User” будет понимание, что вообще принимает функция на входе.

Но если вы попробуете просто написать “as User”, у вас будет ошибка.

describe('processUserTest', () => {
  it('should return text with error', () => {
   const partialUser = { isActive: "true", id: 1 } as User;

    const result processUser(partialUser);
    expect(result).to.eq("Что вы мне подснули?!");
  });
});


Conversion of type '{ isActive: string; id: number; }' to type 'User' 
may be a mistake because neither type sufficiently overlaps with the other. 
If this was intentional, convert the expression to 'unknown' first.
Type '{ isActive: string; id: number; }' 
is missing the following properties from type 'User': name, email, age.


TypeScript использует структурную типизацию, что значит, что два типа считаются совместимыми, если структуры их свойств совместимы, а в примере, isActive не совместим, так как передается строка.

Таким образом проблему решит двойное утверждение (as unknown as User) с помощью этого получится "обмануть" TypeScript, чтобы он полностью проигнорировал все виды проверок и преобразований типов. По сути, это говорит TypeScript:
"Сначала забудь, что ты знаешь об этом объекте, а потом согласись, что он соответствует моему типу".

📌 Итог

Старайтесь не переопределять тип, но если драки с типами не избежать, делайте это грамотно, без any.

💬 Делитесь своим мнением в комментариях👇! Если вам понравилась статья, не забудьте поставить лайк! 👍

#TYPESCRIPT
Хотите больше таких постов?
Подпишитесь на канал и читайте продолжение в Telegram.
Подписаться на @ivanchikovitclub Открыть пост в Telegram