'unknown' против 'any': в чем разница?
Описание проблемы:
В TypeScript 3.0 был введен новый тип unknown
. Согласно официальной документации:
unknown
теперь является зарезервированным названием типа, так как это встроенный тип. В зависимости от вашего предполагаемого использованияunknown
, вы можете удалить объявление этого типа полностью (предпочтя новый типunknown
), или переименовать его во что-то другое.
Возникает вопрос: в чем разница между типами unknown
и any
? Когда стоит использовать unknown
, а когда — any
?
Мне нужно понять, как правильно использовать эти два типа и какие преимущества предоставляет unknown
по сравнению с any
.
5 ответ(ов)
Они различаются по семантике.
unknown
— это родительский тип для всех других типов. Он представляет собой обычный тип в системе типов.
any
означает "отключить проверку типов". Это директива для компилятора.
В TypeScript существуют два типа, которые часто путают: any
и unknown
. Давайте разберем их различия.
any
:- Позволяет присваивать любое значение.
- Позволяет вызывать любые методы, так как компилятор не осуществляет проверку типов.
const a: any = 'a'; // OK
const v1: string = a; // OK, так как a имеет тип any
a.trim(); // OK, так как a имеет тип any
unknown
:- Позволяет присваивать любое значение, но не допускает использования этого значения без предварительной проверки типа.
- Не позволяет вызывать методы, так как компилятор требует явной проверки типа перед использованием.
const b: unknown = 'b'; // OK, так как можем присваивать любое значение
const v2: string = b; // ERROR, нельзя присвоить unknown в string без проверки
const v3: string = b as string; // OK, явное приведение типа
b.trim(); // ERROR, нельзя вызвать метод на unknown
Таким образом, any
дает полную свободу в манипуляциях, но и отнимает контроль типов, в то время как unknown
требует явной проверки или приведения типа, что делает код более безопасным, но менее удобным в некоторых случаях.
Тип any
:
Тип any
представляет собой все возможные значения в JavaScript. Каждый тип может быть присвоен типу any
. Таким образом, тип any
является универсальным суперклассом системы типов. Компилятор TypeScript разрешает любые операции над значениями типа any
. Например:
let myVar: any;
myVar[0];
myVar();
myVar.length;
new myVar();
Во многих случаях такая гибкость компилятора TypeScript оказывается слишком излишней. То есть, он позволяет выполнять операции, которые могут привести к ошибкам во время выполнения.
Тип unknown
:
Тип unknown
также представляет, как и any
, все возможные значения JavaScript. Каждый тип может быть присвоен типу unknown
. Таким образом, тип unknown
является еще одним универсальным суперклассом системы типов (вместе с any
). Однако компилятор TypeScript не будет разрешать любые операции над значениями типа unknown
. Более того, тип unknown
может быть присвоен только типу any
. Пример лучше проиллюстрирует это:
let myVar: unknown;
let myVar1: unknown = myVar; // Ошибки нет
let myVar2: any = myVar; // Ошибки нет
let myVar3: boolean = myVar; // Тип 'unknown' нельзя присвоить типу 'boolean'
// Следующие операции над myVar все вызывают ошибку:
// Объект имеет тип 'unknown'
myVar[0];
myVar();
myVar.length;
new myVar();
Таким образом, по сравнению с типом any
, тип unknown
обеспечивает большую строгость и безопасность, потому что требует явного приведения типов перед выполнением операций.
Я, возможно, запоздал с ответом, но постараюсь внести ясность.
В приведённом вами коде мы имеем дело с двумя переменными:
const canBeAnything: any = 100;
const canNotBeAnything: unknown = 100;
Переменная canBeAnything
имеет тип any
, что означает, что мы можем использовать любую операцию с ней без ограничений TypeScript. Например, метод .startsWith()
можно вызвать без проблем:
canBeAnything.startsWith('10'); // здесь нет ошибки
Однако при работе с переменной типа unknown
, как в случае canNotBeAnything
, мы сталкиваемся с ограничениями. Если мы попытаемся вызвать тот же самый метод:
canNotBeAnything.startsWith('10'); // ошибка: Свойство 'startsWith' не существует на типе 'unknown'
Это связано с тем, что unknown
- это безопасный тип, который требует явного указания типа перед использованием. Чтобы использовать .startsWith()
, нам необходимо указать компилятору, что мы уверены, что переменная является строкой:
(canNotBeAnything as string).startsWith('10'); // Успокой компилятор TS, я знаю, что делаю.
Этот подход позволяет избежать ошибок компиляции, но очень важно помнить, что при этом необходимо быть крайне осторожным. Если canNotBeAnything
действительно является числом, как в нашем примере, то код приведёт к ошибке во время выполнения:
// Ошибка во время выполнения
Итак, хотя использование приведения типа позволяет избежать проблем на этапе компиляции, это может привести к непредсказуемым ошибкам во время выполнения, если вы не уверены в реальном типе переменной. Важно соблюдать осторожность и по возможности избегать приведения типов без необходимости.
В более простых терминах:
any
позволяет делать всё, что угодно.unknown
не позволяет делать ничего, пока вы не проверите тип.
Например:
let a: any;
let b: unknown;
a.anything(); // без ошибки
b.anything(); // ошибка: 'b' имеет тип 'unknown'.
any
отключает все проверки типов, которые выполняет TypeScript, в то время как unknown
заставляет вас быть очень строгими с любым типом, который вы присваиваете.
Как преобразовать строку в число в TypeScript?
Как динамически назначить свойства объекту в TypeScript?
Как удалить элемент массива в TypeScript?
Возможно ли расширять типы в TypeScript?
Что такое "*.d.ts" в TypeScript?