Перейти к содержимому
Теория · Простые и особые типы · урок 5 из 8

Тип unknown

Посылка со штампом «не досматривать» проходит границу без проверки, и беда обнаруживается потом. У таможни есть и другой порядок. Коробку, о которой ничего не известно, пропускают в страну опечатанной. Её можно хранить на складе и передавать дальше, но пользоваться содержимым нельзя, пока коробку не вскроют и не проверят.

В TypeScript опечатанная коробка — тип unknown. Положить в него можно что угодно, а достать — только после проверки:

TypeScript
const value: unknown = JSON.parse('"hello"');
console.log(value.toUpperCase()); // ✗
TEXT
error TS18046: 'value' is of type 'unknown'.

Сообщение говорит: у value тип unknown, и пока он такой, tsc не даст вызвать метод. Внутри на самом деле строка "hello", но компилятор этого не знает и не делает вид, что знает.

Положить можно что угодно

В переменную типа unknown подходит любое значение: число, строка, null, объект. Ограничения начинаются, когда значение хотят использовать:

TypeScript
let box: unknown = 5;
box = "five";
box = null;
const count: number = box; // ✗
TEXT
error TS2322: Type 'unknown' is not assignable to type 'number'.

Разберём пример по строкам:

  1. box объявлена с типом unknown, в ней число 5.
  2. Строку "five" компилятор пропускает.
  3. null тоже подходит.
  4. А положить box в переменную типа number нельзя: компилятор не знает, число ли там сейчас.

С unknown запрещено всё, что any разрешает без вопросов:

  • вызывать методы;
  • читать свойства;
  • класть значение в переменную конкретного типа, например number.

Проверка открывает коробку

Вскрыть коробку помогает обычная проверка JavaScript — оператор typeof. tsc читает условие и внутри if считает значение строкой:

TypeScript
const value: unknown = JSON.parse('"hello"');
if (typeof value === "string") {
    console.log(value.toUpperCase()); // HELLO
}

Внутри if у value тип string, поэтому вызов toUpperCase() проходит проверку. Если бы JSON.parse вернул число, условие не выполнилось бы, и программа не упала бы на вызове.

Так же работают проверки других типов. После typeof value === "number" значение внутри if получает тип number, после typeof value === "boolean" — тип boolean. Проверка открывает коробку ровно для того типа, который вы назвали.

В виджете одно и то же значение проходит три этапа. Следите за подсказкой: с any вызов метода пропускают без вопросов, с unknown — запрещают, а после проверки typeof — снова разрешают.

Вживуюany, unknown и проверка typeofшаг 1 из 6

JSON.parse возвращает any: он не знает, что лежит в строке.

Нажимайте «Следующий шаг»: подсказка покажет тип или ошибку на выделенной строке

Здесь аналогия с опечатанной коробкой перестаёт работать. Настоящую пломбу видно и в стране, а unknown существует только для tsc. В JavaScript-файле от него ничего не остаётся, и в переменной лежит обычная строка.

unknown или any

На собеседовании часто спрашивают: «Чем unknown отличается от any?» Ответ укладывается в три пункта:

  • в оба типа можно положить любое значение;
  • any разрешает со значением всё без проверки, и ошибки всплывают при запуске;
  • unknown не разрешает ничего, пока значение не проверено, и ошибки ловит tsc.

Отсюда правило: если вы не знаете, что придёт, — например, результат JSON.parse, — берите unknown и проверяйте значение перед использованием.

Частая ошибка

Проверили, что значение есть, но не проверили, что это строка:

TypeScript
const value: unknown = JSON.parse('"hello"');
if (value) {
    console.log(value.toUpperCase()); // ✗
}
TEXT
error TS2339: Property 'toUpperCase' does not exist on type '{}'.

Условие if (value) отсекает только null, undefined и другие ложные значения. Внутри if компилятор знает лишь, что значение не null и не undefined: это и значит тип {}. Там может оказаться число или объект, поэтому метод строки вызвать нельзя. Нужна проверка typeof value === "string".

Главное

  • В unknown можно положить любое значение, но использовать его можно только после проверки.
  • Без проверки tsc сообщает error TS18046: 'value' is of type 'unknown'.
  • После проверки typeof value === "string" значение внутри if имеет тип string.
  • any выключает проверку, а unknown заставляет проверить значение, поэтому данные извне читают как unknown.