Menu

TypeScript as: asercje typów i rzutowanie

Słowo kluczowe as każe TypeScriptowi traktować wartość jako inny typ. To nie jest rzutowanie: w czasie wykonania nic nie jest konwertowane ani sprawdzane. Poznaj składnię as i nawiasów ostrych, na co pozwala kompilator, podwójne asercje przez unknown i sytuacje, w których lepszym narzędziem jest type guard.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

value as Type to asercja typu: mówi TypeScriptowi, żeby traktował value jako Type. Często nazywa się to rzutowaniem, ale to tylko instrukcja dla kompilatora. Znika z wyjściowego JavaScriptu, niczego nie konwertuje i niczego nie sprawdza w czasie wykonania.

To typowe użycie: wiesz o wartości więcej, niż może wiedzieć kompilator (tutaj znasz kształt jakiegoś JSON-a), i to mówisz. Jeśli się mylisz, nic cię nie ostrzeże. Kolejne sekcje pokazują, co to oznacza i kiedy lepszym wyborem jest sprawdzenie w czasie wykonania.

as i składnia z nawiasami ostrymi

Tę samą asercję można zapisać na dwa sposoby:

const someValue: unknown = "hello";
const a = someValue as string;   // as syntax
const b = <string>someValue;     // angle-bracket syntax, same meaning

Forma z nawiasami ostrymi jest niedozwolona w plikach .tsx, gdzie <string> zostałoby odczytane jako tag JSX. Używaj wszędzie as, a ten problem nigdy się nie pojawi. Asercje wiążą słabo, więc gdy kontynuujesz wyrażenie, otocz je nawiasami: (value as string).length.

Asercje nie konwertują wartości

To ta część, która powoduje prawdziwe błędy. Asercja zmienia to, co kompilator myśli o wartości, a nie samą wartość:

Kompilator uważa, że asserted to number, więc asserted + 1 przechodzi sprawdzanie typów jako działanie arytmetyczne. W czasie wykonania to nadal string "42" i JavaScript łączy stringi. Żeby zmienić typ wartości, przekonwertuj ją: Number(x), String(x), Boolean(x), BigInt(x), new Date(x). Strona string na number porównuje funkcje konwersji.

ChceszNapiszEfekt w czasie wykonania
Powiedzieć kompilatorowi typ, który znaszx as Tbrak
Zamienić string na liczbęNumber(x), parseInt(x, 10)konwertuje
Zamienić cokolwiek na stringString(x), `${x}`konwertuje
Najpierw sprawdzić typtype guard, typeof, instanceofsprawdza

Na co pozwala kompilator

as nie jest nieograniczone. TypeScript pozwala na x as T, gdy jeden typ da się przypisać do drugiego: poszerzanie ("a" as string, dog as Animal) i zawężanie (animal as Dog, unknown as User) są w porządku. Gdy typy w ogóle się nie pokrywają, kompilator odmawia:

Kompilator zgłasza 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. Sam komunikat wskazuje furtkę: input as unknown as number. Ta podwójna asercja się kompiluje i w czasie wykonania jest dokładnie tak samo błędna jak powyższy przykład. Gdy czujesz, że jej potrzebujesz, właściwą poprawką jest zwykle konwersja (Number(input)) albo inny typ.

Reguła pokrywania się typów jest luźna dla obiektów. Literał obiektu, który ma część właściwości, jest akceptowany i w ten sposób as po cichu przepuszcza niekompletne obiekty:

Adnotacja (const draft: User = { name: "Ada" }) albo satisfies User zgłosiłyby brakujące email (TS2741). Używaj as na literale obiektu tylko wtedy, gdy naprawdę zamierzasz uzupełnić go później, a najlepiej od razu buduj kompletny obiekt.

as const to coś innego

as const wygląda jak asercja, ale robi coś przeciwnego do luzowania: sprawia, że literał jest tak wąski, jak to możliwe. Stringi pozostają typami literałów, tablice stają się krotkami readonly, a właściwości obiektów stają się readonly.

To jest bezpieczne, bo dokładnie opisuje literał, zamiast twierdzić coś, czego kompilator nie widzi. (sizes as readonly string[] w isSize to asercja poszerzająca, też bezpieczna: pozwala includes przyjąć dowolny string.) Więcej na stronie o typach literałów.

Kiedy lepszym narzędziem jest type guard

as to twierdzenie, a type guard to sprawdzenie. Na granicy, gdzie dane przychodzą spoza twojego kodu (JSON, fetch, localStorage, dane od użytkownika, wiadomość), twierdzenie może być fałszywe, a asercja zamienia czytelny błąd na granicy w mylący błąd gdzieś indziej.

Z grubsza, jak dobrać narzędzia, które wyglądają podobnie:

NarzędzieSprawdza w czasie kompilacjiSprawdza w czasie wykonaniaUżywaj, gdy
Adnotacja const x: T = ...tak, w pełniniesam budujesz wartość
satisfies Ttak, w pełni, zachowuje wywnioskowany typnieliterały obiektów, konfiguracja
as Ttylko "czy typy się pokrywają"niewiesz więcej niż kompilator
x!usuwa tylko null / undefinedniewiesz, że wartość jest ustawiona
Type guard x is Tciało guarda to zwykły kodtakdane z zewnątrz

Zostają dwa dobre zastosowania as: zawężenie czegoś, czego kompilator nie potrafi prześledzić (wpis w Map ustawiony dwie linie wcześniej, wartość z nieotypowanej biblioteki), i kod testów, który buduje częściowe dane testowe. Trzymaj je w małej skali i blisko miejsca, w którym wiesz, że twierdzenie jest prawdziwe.

Najczęściej zadawane pytania

Co robi as w TypeScript?

value as Type to asercja typu: mówi kompilatorowi, żeby od tego miejsca traktował value jako Type. Znika ze skompilowanego JavaScriptu, więc nie wykonuje żadnej konwersji ani sprawdzenia w czasie wykonania. Jeśli asercja jest błędna, program zawiedzie później, tam, gdzie zostanie użyty zły typ.

Jak rzutować typ w TypeScript?

TypeScript nie ma rzutowania w czasie wykonania. Użyj as (albo starszego <Type>value), żeby zmienić typ statyczny, gdy wiesz więcej niż kompilator. Żeby naprawdę przekonwertować wartość, wywołaj funkcję: Number("42"), String(42), Boolean(x), new Date(text).

Co oznacza "as unknown as" w TypeScript?

To podwójna asercja. TypeScript odrzuca x as T, gdy dwa typy w ogóle się nie pokrywają (błąd TS2352), a przejście najpierw przez unknown omija to sprawdzenie, bo do unknown i z unknown można zrobić asercję czegokolwiek. Całkowicie wyłącza to sprawdzanie typów dla tej wartości, więc zostaw to dla testów i kodu, w którym typ został sprawdzony w inny sposób.

Czym różni się as od nawiasów ostrych w TypeScript?

Znaczeniem niczym: <string>value i value as string to ta sama asercja. Formy z nawiasami ostrymi nie da się użyć w plikach .tsx, bo koliduje z JSX, dlatego wszyscy używają as.

Czym różni się as od satisfies?

as nadpisuje wywnioskowany typ i sprawdza bardzo mało (brakujące właściwości są dozwolone). satisfies sprawdza wartość względem typu, zgłasza brakujące albo nadmiarowe właściwości i zachowuje dokładny wywnioskowany typ. Dla literałów obiektów wybieraj satisfies, a as tylko wtedy, gdy naprawdę wiesz więcej niż kompilator.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ