본문 바로가기
language/typescript

🧠 타입 추론과 단언 – as, !, ??, ??= 완전 정리

by 죄니안죄니 2025. 4. 27.
📚 타입스크립트 입문 시리즈
🟢 ① tsconfig.json 완전 정복 | 🟢 ② 개발 환경 세팅 | 🟢 ③ 기본 타입 정복 | 🟢 ④ interface vs type | 🟢 ⑤ 함수 타입 선언 | 🟢 ⑥ 유니언 & 인터섹션 타입 | 🔵 ⑦ 타입 추론과 단언

🧠 타입 추론과 단언 – as, !, ??, ??= 완전 정리

TypeScript는 매우 똑똑한 타입 추론 시스템을 가지고 있습니다.
하지만 모든 상황을 100% 정확히 추론하지는 않기 때문에, 개발자가 타입을 직접 단언해야 할 때도 있습니다.

이번 글에서는 다음 4가지 문법을 중심으로 정리해볼게요:

  • as: 타입 단언
  • !: non-null 단언
  • ??: null 병합 연산자
  • ??=: null 병합 할당 연산자 (TS 4.0+)

① 타입 추론이란?

TS는 코드의 구조를 보고 자동으로 타입을 "추론"합니다. 

let name = '홍길동'; // name: string
let age = 25;        // age: number

let이나 const로 값을 할당하면, 그 값을 기준으로 타입이 자동 지정됩니다.
하지만 복잡한 DOM, 외부 API, unknown 타입 등에서는 추론이 정확하지 않을 수 있어요.


② as – 타입 단언 (Type Assertion)

as는 타입스크립트에게 "내가 타입을 안다!"고 알려주는 방식입니다. TS는 항상 안전한 타입만 허용하는데, 우리가 실제로 이 값의 타입을 확실히 아는 경우, TS에게 설명하는 용도로 사용합니다.

📌 예제 1: DOM 요소

const input = document.querySelector('#username');
input.value = 'hi'; // ❌ TS 에러

이유: querySelector의 반환 타입은 Element | null이라서

value 속성이 없다고 생각해요.

✅ 해결법:

const input = document.querySelector('#username') as HTMLInputElement;
input.value = 'hi'; // ✅ value 접근 가능

📌 예제 2: 객체를 특정 타입으로 단언

type User = { name: string; age: number };

const data = JSON.parse('{"name":"홍길동","age":30}') as User;
console.log(data.name); // ✅ 타입스크립트는 User로 인식

 

주의: as 단언은 타입을 바꾸는 게 아니라, 알려주는 것입니다. 실제 타입이 맞지 않으면 런타임 오류가 날 수 있어요!

❌ 잘못된 예

const value = 'hello' as number; // 😱 말도 안 되지만 컴파일은 됨!
  • 타입스크립트는 그냥 "그래~ 니가 맞겠지…" 하고 믿어버려요.
  • 런타임 오류로 이어질 수 있음.

💡 그래서 as는 정말 확실할 때만 사용해야 합니다.

🔥 보너스: as const 도 있어요

const status = 'success' as const;
  • string이 아니라 'success'라는 리터럴 타입으로 고정됨.
  • 주로 readonly, switch, props 등에 사용.

③ ! – Non-null 단언

값이 null이나 undefined일 수 있지만, "절대 아니야!"라고 단언할 때 사용합니다.

const el = document.getElementById('app')!;
el.innerHTML = 'Hello'; // null 체크 없이 사용 가능

!를 붙이면 TS는 null 가능성을 무시합니다. 하지만 **조심해서 사용해야 합니다.** (null이면 런타임 에러!)


④ ?? – Null 병합 연산자

??null이나 undefined인 경우에만 기본값을 사용합니다.

const name = userInput ?? 'Guest';

예시:

const a = null ?? '기본값'; // '기본값'
const b = 0 ?? 100;         // 0 (null 아님!)

false, 0, 빈 문자열은 그대로 유지되기 때문에 ||보다 더 안전합니다.

 

📌 || (OR 연산자)

  • 왼쪽 값이 Falsy면 오른쪽 값이 반환됨
  • Falsy란? → false, 0, ''(빈 문자열), null, undefined, NaN
console.log(false || '대체값');  // 👉 '대체값'
console.log(0 || 100);           // 👉 100
console.log('' || '기본');       // 👉 '기본'

➡️ 이건 우리가 의도한 것과 다르게 작동할 수 있음 (너무 폭넓게 대체를 함)

반면, 

?? 연산자 :  false, 0, '' 같은 값은 있는 값으로 인정됨. 정말로 값이 없을 때만 대체값을 쓴다.


⑤ ??= – Null 병합 할당 연산자

TS 4.0+부터 지원되며, 변수에 null/undefined일 때만 값을 할당합니다.

let config: string | undefined;
config ??= '기본설정'; // config에 할당된 값이 없으니까 undefined니까 '기본설정'을 할당함
console.log(config); // 👉 '기본설정'

let config = '설정A';
config ??= '기본설정'; // 값이 이미 있으면 변경 안 함
console.log(config); // 👉 '설정A' (변경 안 됨)

let num = 0;
num ??= 100; // falsy한 값은 유지됨 
console.log(num); // 👉 0 (0은 null/undefined가 아니니까 유지됨)

기존 값이 null 또는 undefined일 때만 새로운 값을 할당합니다.


📐 요약

문법 용도 예시
as 타입 단언 el as HTMLInputElement
! non-null 단언 el!
?? null 대체 value ?? 'default'
??= null일 때만 할당 config ??= 'init'

📘 다음 글 예고

👉 타입 좁히기 (Narrowing) - typeof, in, instanceof 활용 에서 유니언 타입을 분기처리하는 핵심 문법들을 살펴봅니다.

 

댓글