DevGang
Авторизоваться

Вы, наверное, знаете эту ошибку, но почему она всегда проскальзывает?

TypeScript не улавливает это в строгом режиме

Эта ошибка выглядит знакомо?

Попытка доступа к неопределенным объектам — одна из самых частых ошибок в JavaScript.

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

И я не говорю здесь о небрежном использовании TypeScipt, например, о добавлении любого, использовании @ts-ignore или утверждений типа с помощью as string...

Я говорю об использовании TypeScript без исключений в строгом режиме.

То, как его следует использовать. И этот баг все равно проскочит.

Почему это?

И что вы можете сделать, чтобы сделать ваш код более надежным?

Почему TypeScript это пропускает?

Начнем с простых примеров:

// 1. Arrays

const array: { id:string }[] = []
//...
console.log(array[0].id) // this throws an error but won't be caught by TS


// 2. Objects

const object: Record<string, string> = {}
//...
console.log(object['randomKey'].length) // this throws an error but won't be caught by TS

В обоих сценариях будет выдана ошибка, но большинство настроек TypeScript ее не предвидят.

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

Есть варианты борьбы с этим, но у каждого из них есть свои минусы.

Что ты можешь сделать?

Тестирование

Хотя вы потенциально можете обнаружить эту ошибку в тесте, вы не можете полагаться только на нее.

Это один из тех случаев, когда 100% покрытие ни о чем не говорит.

Массив, определенный по индексу 0, может быть неопределенным по индексу 1. Вам придется протестировать все возможные индексы, что буквально невозможно.

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

MAP

Используйте карты вместо объектов или массивов. Карты можно использовать аналогично объектам или массивам, но с безопасным типом.

// instead of this:
const object: Record<string, string> = {}
console.log(object['randomKey'].length) // throws error

// do this:
const map = new Map<string, string>()
const str = map.get('randomKey') // returns string | undefined
console.log(str.length) // TS catches this, because str could be undefined

Однако одним из больших недостатков использования карты является то, что ее необходимо преобразовать в объект, прежде чем ее можно будет сериализовать в JSON и наоборот.

Это может замедлить работу вашего приложения при работе с большими наборами данных и создать дополнительный уровень сложности.

Другая причина против карт заключается в том, что массивы и объекты проще в использовании и менее многословны.

Выйдите за рамки строгости

В версии 4.2 появилась опция компилятора TypeScript под названием noUncheckedIndexedAccess.

// tsconfig.json
{
  // ...
  "compilerOptions": {
    // ...
    "noUncheckedIndexedAccess":true
  }
}

Эта опция будет предупреждать вас всякий раз, когда вы получаете доступ к свойству через индекс.

const array: { id:string }[] = []
//...
console.log(array[0].id) // TS will warn you: 'array[0]' is possibly 'undefined'.

Решено?

Частично.

Как упоминалось ранее, компилятор TypeScript имеет свои ограничения... А добавление noUncheckedIndexedAccess приводит к множеству ложных срабатываний:

const array : {id: string}[] = [ { id: 'id1' }]
console.log(array[0].id) // this throws a TS error, even though array[0] is defined

// this won't work either
if(array.length > 0) console.log(array[0].id) // throw TS error

// also C-style loops won't work anymore
for (let i = 0; i < array.length; i++) {
    console.log(array[i].id); // throws TS error
}

Это личное предпочтение, если вы хотите дважды проверять каждый индексированный доступ, даже если иногда вы уже уверены, что он определен. Вот почему эта опция отключена в строгом режиме.

Решение? Комбинация

Ошибка «невозможно прочитать свойство неопределенного» — это реальная вещь, которую все время упускают. Проблема в том, что этот баг сложно обнаружить и даже тесты здесь не спасут.

Чтобы сделать ваш код более надежным, вы можете использовать комбинацию перечисленных выше инструментов.

В любом случае используйте тесты и возможно поймаете в сети что-то такое, чего не ожидали.

Рассмотрите карты, где они имеют смысл в вашей кодовой базе.

И используйте опцию компилятора TypeScripts noUncheckedIndexedAccess в качестве дополнительной страховки.

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

Примечание

Нужна профессиональная поддержка по вашему проекту Vue.js или Nuxt.js? Свяжитесь со мной через https://nuxt.wimadev.de

Источник:

#TypeScript
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться