본문으로 건너뛰기

as는 검사기에게 하는 말

타입스크립트 목차바깥에서 온 값 — any·unknown·as레슨 29 / 51

목차

as 타입(타입 단언)은 값을 바꾸지도 확인하지도 않고, 검사기에게 '이 값을 이 타입이라고 믿어라' 하고 말하는 문법입니다. 실행하기 전에 다른 타입 글자와 마찬가지로 빈칸으로 지워지므로, 실제 값의 모양이 다르면 실행할 때 어긋납니다.

unknown — 확인하고 쓰기에서 unknown으로 받은 값은 확인하기 전까지 쓸 수 없었습니다. 이때 확인 코드를 쓰는 대신 as를 붙여 검사만 넘기면 어떻게 되는지 보고, 검사를 덮는 네 가지 길을 한자리에 모아 비교합니다.

JS 다시 보기: Number('4500')은 글자 '4500'을 수 4500으로 바꿉니다(JS '글자를 수로: Number와 NaN').

5단계 중 1단계. 4번 줄: 받은 글 안의 price는 수 4500이 아니라 글자 "4500"입니다.

1as는 검사기에게 믿으라고만 말한다

그림의 코드는 JSON 글을 읽은 뒤 as Item을 붙여, data가 { price: number } 모양이라고 단언합니다. 그런데 받은 글의 price는 수 4500이 아니라 따옴표가 붙은 글자 "4500"입니다.

interface Item {
  price: number;
}
const text = '{"price":"4500"}';
const data =
  JSON.parse(text) as Item;
console.log(data.price + 500);
검사 결과 오류 없음
실행 결과
4500500

검사 결과는 오류 없음입니다. 6번 줄의 as Item을 보고 검사기는 data의 타입을 Item으로 믿고, 7번 줄의 data.price도 number로 봅니다. 그러나 타입은 실행 전에 지워진다에서 보았듯, Node는 실행하기 전에 1~3번 줄의 interface Item과 6번 줄의 as Item을 모두 빈칸으로 지웁니다. 남은 자바스크립트가 돌 때 data.price에 들어 있는 값은 글자 '4500' 그대로여서, + 500이 덧셈 대신 이어 붙이기가 되어 4500500을 찍습니다.

2as는 값을 바꾸지 않는다

다른 언어의 형변환(캐스팅)을 본 적이 있다면 '4500' as number가 글자를 수로 바꿔 줄 것처럼 보이기 쉽습니다. 직접 적어 보면 검사기가 막습니다.

const n = '4500' as number;
검사 결과 오류 1개
main.ts(1,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.

오류 문장 TS2352는 두 타입(string과 number)이 겹치는 값이 하나도 없어서(sufficiently overlaps) 실수일 것 같다고 알려 줍니다. 1절의 JSON.parse(text) as Item⁠이나 앞 레슨의 (data as Menu)가 지나갔던 까닭은, any와 unknown이 모든 타입과 겹치기 때문입니다. 이미 string으로 아는 값을 전혀 다른 number라고 우기면 as도 막습니다.

오류 문장 첫머리의 영문 단어 Conversion(변환)에 속으면 안 됩니다. as는 실행할 때 값을 바꾸지 않습니다. 글자 '4500'을 실제 수 4500으로 바꾸려면 자바스크립트의 Number 함수를 써야 합니다.

const n = Number('4500');
console.log(n + 500);
실행 결과
5000

Number('4500')은 실행할 때 실제로 돌아 수 4500을 돌려주고, 검사기도 그 결과 타입이 number임을 압니다.

3검사를 덮는 네 가지 길

as는 타입이 다를 때뿐 아니라 속성이 아예 없을 때도 검사를 통과시킵니다. 다음 코드의 결과를 먼저 예상해 보세요.

interface Menu {
  name: string;
  price: number;
}
const text = '{"price":4500}';
const m =
  JSON.parse(text) as Menu;
console.log(
  m.name.toUpperCase(),
);

검사를 지나갈까요? 지나가지 못하면 몇 번 줄에서 막힐까요? Node로 돌리면 무엇이 찍힐까요?

검사 결과와 실행 결과 보기
검사 결과 오류 없음
실행 결과
main.ts:9  m.name.toUpperCase(),         ^ TypeError: Cannot read properties of undefined (reading 'toUpperCase')    at main.ts:9:10

예상과 다르면 그곳이 배울 곳입니다. 검사는 오류 없이 지나갑니다. 7번 줄의 as Menu 때문에 검사기는 m.name이 string이라고 믿습니다. 실행하면 받은 글에 name이 없어 m.name은 undefined이고, 9번 줄에서 TypeError로 멈춥니다.

지금까지 본 any, unknown, as와 null·undefined 거르기에서 잠깐 본 !(null 아님 단언)까지 묶어 검사 규칙 6을 정리합니다.

검사 규칙 6. 검사기는 코드에 쓴 것만 믿습니다. any는 검사를 끄고, unknown은 확인해 좁히기 전에는 쓰지 못하게 막습니다. as 타입과 !는 값을 바꾸거나 확인하지 않고, 검사기에게 그 타입이라고 믿으라고만 말합니다.

검사 오류를 확인 없이 덮는 네 가지 길과 대신 쓸 바른 길을 한 표에 모았습니다.

덮는 길검사기에게 하는 말실행할 때대신 쓸 길
any이 값과 꺼낸 값의 검사를 꺼라적은 : any는 빈칸으로 지워짐: unknown으로 받아 확인
as 타입이 값을 그 타입이라고 믿어라빈칸으로 지워짐확인 함수(직접 만드는 확인 함수) · 값 변환은 Number()
값!null·undefined가 아니라고 믿어라빈칸으로 지워짐if로 거르기 · ?. · ??(null·undefined 거르기)
// @ts-ignore바로 아래 한 줄의 오류를 지워라주석이라 무시됨오류 원인을 찾아 값이나 타입을 고침

예제

검사 결과와 실행 결과를 먼저 적어 본 뒤 펼쳐 보세요. 맞혀 보기는 점수에 들지 않습니다.

선택 속성 memo?: string 뒤에 !를 붙여 length를 읽는 코드입니다.

interface Order {
  memo?: string;
}
const o: Order = {};
console.log(o.memo!.length);

검사를 지나갈까요? 지나가지 못하면 몇 번 줄에서 막힐까요? Node로 돌리면 무엇이 찍힐까요?

검사 결과와 실행 결과 보기
검사 결과 오류 없음
실행 결과
main.ts:5console.log(o.memo .length);                    ^ TypeError: Cannot read properties of undefined (reading 'length')    at main.ts:5:21

예상과 다르면 그곳이 배울 곳입니다. o.memo의 타입은 string | undefined이지만, !가 undefined를 빼고 string이라고 믿으라고 말해서 검사는 지나갑니다. Node의 오류 출력을 보면 o.memo!.length의 ! 자리가 빈칸(o.memo .length)으로 지워져 있습니다. o.memo는 undefined 그대로라 5번 줄에서 멈춥니다. o.memo?.length ?? 0처럼 확인하는 식을 써야 안전합니다.

자주 틀리는 곳

틀린 생각: as number를 붙이면 글자 값이 수로 바뀐다.

TS2352 오류 문장에 Conversion이라는 단어가 있어 값을 바꿔 주는 연산자처럼 보입니다.

맞는 규칙: as는 실행 전에 지워지므로 값을 조금도 바꾸지 않습니다(검사 규칙 1 · 6). 실행할 때 글자를 수로 바꾸려면 Number(text)를 씁니다(2절).

틀린 생각: as Item을 붙이면 검사기가 값이 Item 모양인지 확인해 준다.

검사 오류가 사라지니 값이 맞는지 확인된 것처럼 느껴집니다.

맞는 규칙: as Item은 확인을 하는 것이 아니라 검사기에게 확인하지 말고 믿으라고 하는 말입니다(1 · 3절). 실제 값을 확인하는 코드는 다음 레슨 직접 만드는 확인 함수에서 직접 작성합니다.

틀린 생각: TS2352 오류 문장이 알려 준 대로 as unknown as number를 쓰면 해결된다.

2절의 TS2352 오류 끝 문장에는 먼저 unknown으로 바꾼 뒤 단언하라는 말(convert ... to 'unknown')⁠이 적혀 있습니다. 그대로 따라 쓰면 검사는 지나갑니다.

const n =
  '3' as unknown as number;
console.log(n + 1);
검사 결과 오류 없음
실행 결과
31

'3'을 모든 타입과 겹치는 unknown으로 먼저 단언한 뒤 다시 number로 단언해 검사기의 눈만 가린 것입니다. 실행하면 as unknown as number는 통째로 지워지고 '3' + 1이 되어 31이 찍힙니다.

맞는 규칙: as unknown as T는 두 번 연속으로 검사기를 속이는 표기입니다. 오류 문장에 적혀 있어도 쓰지 않습니다.

틀린 생각: !는 값이 없을 때 빈값으로 채워 준다.

o.memo!라고 쓰면 undefined 오류가 사라지니, 알아서 빈 문자열 같은 값을 넣어 줄 것 같습니다.

맞는 규칙: !도 as와 똑같이 실행 전에 빈칸으로 지워집니다(예제). 기본값을 채우려면 o.memo ?? ''를 씁니다.

확인 문제

1. 다음 코드는 검사를 지나갈까요? 지나가지 못하면 몇 번 줄에서 막힐까요? Node로 돌리면 무엇이 찍힐까요?

const v: unknown = '700';
const won = v as number;
console.log(won + 300);
힌트

1번 줄 v의 타입이 unknown이라 2번 줄의 v as number가 검사를 지나는지, 그리고 실행할 때 as number가 어떻게 되는지 따로 따져 봅니다.

정답
검사 결과 오류 없음
실행 결과
700300

검사는 오류 없이 지나가고, 실행하면 700300이 찍힙니다. 1번 줄 v가 unknown이라 v as number는 TS2352 없이 검사를 지나고, won의 타입은 number가 됩니다. 그러나 실행할 때 as number는 지워지고 v에 든 글자 '700'이 그대로 남아 '700' + 300이 됩니다. 흔한 오답은 1000입니다. as number가 글자 '700'을 수 700으로 바꿔 준다고 본 것입니다.

2. 글자 '4500'을 실제 수 4500으로 바꾸어 실행 결과 5000이 찍히도록 1번 줄의 빈칸에 알맞은 함수 이름을 채우세요.

const price = ____('4500');
console.log(price + 500);
힌트

이 레슨의 'JS 다시 보기'와 2절에서 글자를 수로 바꿀 때 쓴 자바스크립트 내장 함수입니다.

정답

빈칸은 Number입니다. Number('4500')은 실행할 때 글자를 수 4500으로 바꾸고, 검사기도 price를 number로 추론해 5000이 찍힙니다.

3. 아래 코드의 3번 줄에서 TS18046 오류가 났습니다. 값이 수일 때만 500을 더하고, 글자 '4500'이 들어왔을 때는 '4500500'이 찍히지 않게 막는 고치기를 고르세요.

const raw: unknown =
  JSON.parse('"4500"');
console.log(raw + 500);
검사 결과 오류 1개
main.ts(3,13): error TS18046: 'raw' is of type 'unknown'.
  • ① 3번 줄을 if (typeof raw === 'number') { console.log(raw + 500); }⁠로 바꾼다.
  • ② 3번 줄을 console.log((raw as number) + 500);⁠으로 바꾼다.
  • ③ 1번 줄의 : unknown을 지운다.
힌트

JSON 글 안의 값이 수 4500이 아니라 글자 "4500"일 때, 실행할 때 실제로 확인하는 코드는 어느 것인지 봅니다.

정답

①입니다. 실행할 때 typeof raw === 'number'⁠가 거짓('string')이므로 잘못된 이어 붙이기를 건너뛰고, 갈래 안에서는 raw가 number로 좁혀져 검사도 지납니다.

  • ② as number는 실행 전에 지워져 '4500500'이 그대로 찍힙니다.
  • ③ : unknown을 지우면 raw가 any가 되어 ②와 똑같이 검사를 끄고 '4500500'을 찍습니다.

컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요. 1절 코드의 6번 줄에서 as Item을 지우고 5번 줄 const data 뒤에 : unknown을 붙이면 7번 줄이 막힙니다. 객체 안에 price 속성이 있고 그 값이 수인지 확인하는 함수를 어떻게 만들 수 있을지 생각해 본 뒤, 다음 레슨 직접 만드는 확인 함수에서 답을 확인해 보세요. 검사하고 돌리는 법은 내 컴퓨터에서 검사하고 돌리기에 있습니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

타입스크립트 목차로