- Оригинальный адрес:TypeScript 3.0: The unknown Type
- Оригинальный автор:Marius Schulz
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:shixi-li
- Корректор:Usey95, smilemuffie
TypeScript 3.0 представляет новыйunknown
типа, этоany
Тип соответствует типу ценной бумаги.
unknown
а такжеany
Основное отличиеunknown
Типы будут строже:unknown
Прежде чем выполнять большинство операций над значением типа, мы должны выполнить некоторую проверку. пока правильноany
Нам не нужно делать никаких проверок перед выполнением операции над значением типа.
Эта статья посвященаunknown
видов практического применения, а также в том числеany
Сравнение типов. Если вам нужен более полный пример кода, чтобы понятьunknown
Семантика типов, см. Андерса Хейлсберга.оригинальный запрос на вытягивание.
any
Типы
давайте сначала посмотримany
типы, чтобы мы могли лучше понять импортunknown
Мотивация типа.
Поскольку TypeScript выпустил свою первую версию в 2012 годуany
Типы существовали всегда. Он представляет все возможные значения JavaScript — примитивные типы, объекты, массивы, функции, ошибки, символы и все остальное, что вы можете определить.
В TypeScript любой тип может быть классифицирован как любой тип. Это позволяетany
Типы становятся частью системы типовверхний тип(также известен какглобальный супертип).
Вот некоторые из значений, которые мы присваиваемany
Пример кода типа:
let value: any;
value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK
any
Типы, по сути, являются аварийным люком для системы типов. Это дает нам как разработчикам большую свободу: TypeScript позволяет намany
Значение типа выполняет любую операцию без предварительной проверки.
В приведенном выше примере переменнаяvalue
определяется как типany
. Из-за этого TypeScript считает все следующее корректным для типов:
let value: any;
value.foo.bar; // OK
value.trim(); // OK
value(); // OK
new value(); // OK
value[0][1]; // OK
Во многих сценариях такой механизм является слишком свободным. использоватьany
тип, легко написать код, который имеет правильный тип, но работает ненормально. если мы используемany
type, вы не сможете насладиться большим количеством механизмов защиты TypeScript.
Но что, если бы вы могли иметь типы верхнего уровня, которые по умолчанию были бы безопасными? Этоunknown
Причина прихода.
unknown
Типы
как и все типы можно классифицировать какany
, все типы также можно классифицировать какunknown
. Это делаетunknown
стать еще одним типом верхнего уровня системы типов TypeScript (еще одинany
).
Это тот же набор примеров присваивания, который мы видели ранее, на этот раз с использованием типаunknown
Переменные:
let value: unknown;
value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK
правильноvalue
Все присваивания переменных считаются корректными по типу.
Когда мы пытаемся преобразовать тип вunknown
Что происходит, когда значение присваивается переменной другого типа?
let value: unknown;
let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error
let value4: number = value; // Error
let value5: string = value; // Error
let value6: object = value; // Error
let value7: any[] = value; // Error
let value8: Function = value; // Error
unknown
Типы могут быть присвоены толькоany
тип иunknown
сам тип. Интуитивно это имеет смысл: только контейнеры, которые могут хранить значения любого типа, могут хранитьunknown
Типы значений. Ведь мы не знаем переменныхvalue
Какой тип значения хранится в .
Теперь давайте посмотрим, когда мы попытаемсяunknown
Что происходит, когда операция выполняется над значением . Вот та же операция, которую мы видели раньше:
let value: unknown;
value.foo.bar; // Error
value.trim(); // Error
value(); // Error
new value(); // Error
value[0][1]; // Error
Будуvalue
Тип переменной установлен наunknown
После этого эти операции больше не считаются типокорректными. Путем измененияany
Печататьunknown
Типа, наше значение по умолчанию было изменено с Разрешить все на Разрешение почти ничего.
Этоunknown
Основное ценностное предложение типов: TypeScript не позволяет намunknown
выполнить любую операцию над значением . Вместо этого мы должны сначала выполнить некоторую проверку типов, чтобы сузить тип значения, с которым мы работаем.
уменьшитьunknown
Диапазон типов
Мы можем комбинировать по-разномуunknown
Типы сужаются до более конкретных типов, включаяtypeof
оператор,instanceof
Операторы и защитные функции пользовательского типа. Все эти типы методов сужения вносят свой вклад в TypeScript.Анализ типов на основе потока управления.
Следующий пример иллюстрируетvalue
как через дваif
Более конкретный тип получается в ветке оператора:
function stringifyForLogging(value: unknown): string {
if (typeof value === "function") {
// Within this branch, `value` has type `Function`,
// so we can access the function's `name` property
const functionName = value.name || "(anonymous)";
return `[function ${functionName}]`;
}
if (value instanceof Date) {
// Within this branch, `value` has type `Date`,
// so we can call the `toISOString` method
return value.toISOString();
}
return String(value);
}
Помимо использованияtypeof
илиinstanceof
В дополнение к оператору мы также можем использовать функцию защиты пользовательского типа, сужающуюunknown
Диапазон типов:
/**
* A custom type guard function that determines whether
* `value` is an array that only contains numbers.
*/
function isNumberArray(value: unknown): value is number[] {
return (
Array.isArray(value) &&
value.every(element => typeof element === "number")
);
}
const unknownValue: unknown = [15, 23, 8, 4, 42, 16];
if (isNumberArray(unknownValue)) {
// Within this branch, `unknownValue` has type `number[]`,
// so we can spread the numbers as arguments to `Math.max`
const max = Math.max(...unknownValue);
console.log(max);
}
несмотря на то чтоunknownValue
был классифицирован какunknown
type, обратите внимание, как он все еще извлекается в ветке ifnumber[]
Типы.
правильноunknown
Типы используют утверждения типа
В предыдущем разделе мы видели, как использоватьtypeof
,instanceof
и пользовательские функции защиты типа, чтобы убедить компилятор TypeScript в том, что значение имеет определенный тип. Это безопасный и рекомендуемый способ обозначить «неизвестный» тип как более конкретный тип.
Если вы хотите заставить компилятор типа доверияunknown
имеет заданный тип, вы можете использовать утверждение типа следующим образом:
const value: unknown = "Hello World";
const someString: string = value as string;
const otherString = someString.toUpperCase(); // "HELLO WORLD"
Обратите внимание, что TypeScript на самом деле не выполняет никаких специальных проверок, чтобы убедиться, что утверждения типа действительно действительны. Средство проверки типов предполагает, что вы знаете лучше и верите, что любой тип, который вы используете в утверждении типа, является правильным.
Это может легко привести к возникновению ошибки во время выполнения, если вы сделаете ошибку и укажете неправильный тип:
const value: unknown = 42;
const someString: string = value as string;
const otherString = someString.toUpperCase(); // BOOM
этоvalue
Значение переменной — число, но давайте предположим, что это строка, и используем утверждение типа.value as string
. Поэтому используйте утверждения типов с осторожностью!
в союзном типеunknown
Типы
Теперь давайте посмотрим, как это обрабатывается в типах объединения.unknown
Типы. В следующем разделе мы также узнаем о типах пересечений.
В союзном типеunknown
Типы поглощают любой тип. Это означает, что если любой тип компонентаunknown
, тип объединения также будет эквивалентенunknown
:
type UnionType1 = unknown | null; // unknown
type UnionType2 = unknown | undefined; // unknown
type UnionType3 = unknown | string; // unknown
type UnionType4 = unknown | number[]; // unknown
Неожиданностью этого правила является то, чтоany
Типы. Если хотя бы один тип композицииany
, тип объединения будет эквивалентенany
:
type UnionType5 = unknown | any; // any
так почемуunknown
Может поглощать любой тип (any
кроме типа)? давай подумаемunknown | string
этот пример. Этот тип может представлять любое неизвестное или строковое значение. Как мы узнали ранее, все типы значений можно определить какunknown
типа, который также включает в себя всеstring
типа, следовательно,unknown | string
средства иunknown
Набор значений с самим типом. Таким образом, компилятор может упростить тип объединения доunknown
Типы.
крестового типаunknown
Типы
В перекрестном типе любой тип может быть поглощенunknown
Типы. Это означает объединение любого типа сunknown
Пересечение не меняет тип результата:
type IntersectionType1 = unknown & null; // null
type IntersectionType2 = unknown & undefined; // undefined
type IntersectionType3 = unknown & string; // string
type IntersectionType4 = unknown & number[]; // number[]
type IntersectionType5 = unknown & any; // any
Давайте рассмотримIntersectionType3
:unknown & string
Тип указывает, что все могут быть одновременно назначеныunknown
а такжеstring
значение типа. Поскольку каждый тип может быть присвоенunknown
типа, поэтому в типе пересечения содержитсяunknown
не изменит результат. нам останется толькоstring
Типы.
Использовать тип какunknown
оператор значения
unknown
Значение типа нельзя использовать в качестве операнда большинства операторов. Это связано с тем, что если мы не знаем тип используемого значения, большинство операторов вряд ли дадут осмысленные результаты.
Вы можете использовать тип какunknown
Для значения используются только четыре оператора равенства и неравенства:
===
==
!==
!=
Если типunknown
Если вы используете какой-либо другой оператор для значения, вы должны сначала указать тип (или использовать утверждение типа, чтобы заставить компилятор доверять вам).
Пример: изlocalStorage
читать JSON в
Вот как мы используемunknown
Реальные примеры типов.
Предположим, мы хотим написатьlocalStorage
Функция, которая считывает значение и десериализует его в JSON. Если элемент не существует или является недопустимым JSON, функция должна вернуть результат ошибки, в противном случае она должна десериализоваться и вернуть значение.
Потому что мы не знаем, какой тип значения мы получим после десериализации сохраненной строки JSON. мы будем использоватьunknown
как тип десериализованного значения. Это означает, что вызывающая сторона нашей функции должна выполнить некоторую проверку (или использовать утверждение типа) перед выполнением операции над возвращаемым значением.
Вот как мы реализуем эту функцию:
type Result =
| { success: true, value: unknown }
| { success: false, error: Error };
function tryDeserializeLocalStorageItem(key: string): Result {
const item = localStorage.getItem(key);
if (item === null) {
// The item does not exist, thus return an error result
return {
success: false,
error: new Error(`Item with key "${key}" does not exist`)
};
}
let value: unknown;
try {
value = JSON.parse(item);
} catch (error) {
// The item is not valid JSON, thus return an error result
return {
success: false,
error
};
}
// Everything's fine, thus return a success result
return {
success: true,
value
};
}
тип возвращаемого значенияResult
Являетсяотмеченный тип соединения. В других языках это также может называтьсяMaybe
,Option
илиOptional
. Мы используемResult
наглядно моделировать успешные и неуспешные исходы операций.
tryDeserializeLocalStorageItem
Вызывающий функцию пытается использоватьvalue
илиerror
свойство должно быть проверено в первую очередьsuccess
Атрибуты:
const result = tryDeserializeLocalStorageItem("dark_mode");
if (result.success) {
// We've narrowed the `success` property to `true`,
// so we can access the `value` property
const darkModeEnabled: unknown = result.value;
if (typeof darkModeEnabled === "boolean") {
// We've narrowed the `unknown` type to `boolean`,
// so we can safely use `darkModeEnabled` as a boolean
console.log("Dark mode enabled: " + darkModeEnabled);
}
} else {
// We've narrowed the `success` property to `false`,
// so we can access the `error` property
console.error(result.error);
}
осторожность,tryDeserializeLocalStorageItem
Функция не может просто вернутьnull
чтобы указать, что десериализация не удалась по следующим причинам:
-
null
value является допустимым значением JSON. Поэтому мы не можем различать пары значенийnull
Произошла ли десериализация или вся операция завершилась сбоем из-за отсутствия параметров или синтаксических ошибок. - Если мы вернемся из функции
null
, мы не можем вернуть ошибку одновременно. Следовательно, вызывающая сторона нашей функции не знает, почему операция не удалась.
Для полноты более зрелой альтернативой этому подходу является использованиедекодер типаДелайте безопасный разбор JSON. Декодер требует, чтобы мы указали ожидаемую структуру данных десериализуемого значения. Если сохраненный результат JSON не соответствует этой структуре данных, декодирование завершится ошибкой. Таким образом, наша функция всегда возвращает действительный или ошибочный декодированный результат, не более того.unknown
типа тоже.
Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,товар,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.