value as Type это утверждение типа: оно велит TypeScript считать value типом Type. Его называют приведением, но это лишь указание компилятору. Из JavaScript на выходе оно стирается, ничего не преобразует и ничего не проверяет во время выполнения.
Это типичное применение: вы знаете о значении больше, чем может знать компилятор (здесь форму некоторого JSON), и сообщаете об этом. Если вы ошиблись, ничто не предупредит. В следующих разделах показано, что это значит и когда лучше проверка во время выполнения.
as и синтаксис угловых скобок
У одного и того же утверждения две записи:
const someValue: unknown = "hello";
const a = someValue as string; // as syntax
const b = <string>someValue; // angle-bracket syntax, same meaning
Форма с угловыми скобками не разрешена в файлах .tsx, где <string> читалось бы как тег JSX. Пишите as везде, и вопрос не возникнет. Утверждения связываются слабо, поэтому оборачивайте их в скобки, если продолжаете выражение: (value as string).length.
Утверждения не преобразуют значения
Именно это вызывает реальные ошибки. Утверждение меняет то, что компилятор думает о значении, а не само значение:
Компилятор считает, что asserted это number, поэтому asserted + 1 проходит проверку как арифметика. Во время выполнения это по-прежнему строка "42", и JavaScript склеивает строки. Чтобы изменить тип значения, преобразуйте его: Number(x), String(x), Boolean(x), BigInt(x), new Date(x). Функции преобразования сравниваются на странице строка в число.
| Вы хотите | Пишите | Эффект во время выполнения |
|---|---|---|
| Сообщить компилятору известный вам тип | x as T | никакого |
| Превратить строку в число | Number(x), parseInt(x, 10) | преобразует |
| Превратить что угодно в строку | String(x), `${x}` | преобразует |
| Сначала проверить тип | защитник типа, typeof, instanceof | проверяет |
Что разрешает компилятор
as не безграничен. TypeScript разрешает x as T, когда один тип можно присвоить другому: и расширение ("a" as string, dog as Animal), и сужение (animal as Dog, unknown as User) допустимы. Когда типы совсем не пересекаются, он отказывается:
Компилятор сообщает index.ts(3,11): error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Само сообщение называет лазейку: input as unknown as number. Это двойное утверждение компилируется и во время выполнения ровно так же неверно, как пример выше. Когда вам кажется, что оно нужно, правильное исправление обычно это преобразование (Number(input)) или другой тип.
Для объектов правило пересечения мягкое. Объектный литерал, у которого есть некоторые из свойств, принимается, и так as незаметно пропускает неполные объекты:
Аннотация (const draft: User = { name: "Ada" }) или satisfies User сообщили бы об отсутствующем email (TS2741). Используйте as для объектного литерала, только если действительно собираетесь дозаполнить его позже, а лучше сразу собирайте полный объект.
as const устроен иначе
as const похоже на утверждение, но делает противоположное ослаблению: максимально сужает литерал. Строки остаются литеральными типами, массивы становятся readonly-кортежами, а свойства объектов становятся readonly.
Это безопасно, потому что точно описывает литерал, а не утверждает то, чего компилятор не видит. (sizes as readonly string[] внутри isSize это расширяющее утверждение, тоже безопасное: оно позволяет includes принимать любую строку.) Подробнее на странице литеральные типы.
Когда защитник типа подходит лучше
as это утверждение, а защитник типа это проверка. На границе, где данные приходят извне вашего кода (JSON, fetch, localStorage, пользовательский ввод, сообщение), утверждение может оказаться ложным, и as превращает понятную ошибку на границе в запутанную ошибку где-то в другом месте.
Краткий путеводитель по похожим инструментам:
| Инструмент | Проверка при компиляции | Проверка во время выполнения | Когда использовать |
|---|---|---|---|
Аннотация const x: T = ... | да, полная | нет | вы сами создаёте значение |
satisfies T | да, полная, сохраняет выведенный тип | нет | объектные литералы, конфигурация |
as T | только «пересекаются ли типы» | нет | вы знаете больше компилятора |
x! | только убирает null / undefined | нет | вы знаете, что значение задано |
Защитник типа x is T | тело защитника это обычный код | да | данные извне |
Остаются два хороших применения as: сужение того, за чем компилятор не может проследить (запись Map, которую вы задали двумя строками выше, значение из нетипизированной библиотеки), и тестовый код, который собирает частичные фикстуры. Держите такие места маленькими и рядом с тем местом, где вы знаете, что утверждение верно.
Часто задаваемые вопросы
Что делает as в TypeScript?
value as Type это утверждение типа: оно велит компилятору дальше считать value типом Type. Из скомпилированного JavaScript оно удаляется, поэтому не выполняет ни преобразования, ни проверки во время выполнения. Если утверждение неверно, программа падает позже, там, где используется неверный тип.
Как привести тип в TypeScript?
Приведений во время выполнения в TypeScript нет. Используйте as (или более старый <Type>value), чтобы изменить статический тип, когда вы знаете больше компилятора. Чтобы действительно преобразовать значение, вызовите функцию: Number("42"), String(42), Boolean(x), new Date(text).
Что означает «as unknown as» в TypeScript?
Двойное утверждение. TypeScript отказывается выполнять x as T, когда два типа совсем не пересекаются (ошибка TS2352), а проход через unknown обходит эту проверку, потому что к unknown и из него можно утверждать что угодно. Это полностью отключает проверку типов для значения, поэтому оставьте такой приём для тестов и для кода, где тип проверен другим способом.
Чем as отличается от угловых скобок в TypeScript?
По смыслу ничем: <string>value и value as string это одно и то же утверждение. Форму с угловыми скобками нельзя использовать в файлах .tsx, потому что она конфликтует с JSX, поэтому все пишут as.
Чем as отличается от satisfies?
as переопределяет выведенный тип и почти ничего не проверяет (отсутствующие свойства разрешены). satisfies проверяет значение по типу, сообщая об отсутствующих или лишних свойствах, и сохраняет точный выведенный тип. Для объектных литералов предпочитайте satisfies, а as используйте, только когда действительно знаете больше компилятора.