🟢 ① 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 활용 에서 유니언 타입을 분기처리하는 핵심 문법들을 살펴봅니다.
'language > typescript' 카테고리의 다른 글
📦 제네릭(Generic)의 이해와 사용법 (0) | 2025.04.27 |
---|---|
🔎 타입 좁히기(Narrowing) - typeof, in, instanceof 완전 정리 (0) | 2025.04.27 |
🔀 유니언 타입 vs 인터섹션 타입 – | 와 &의 차이점 완벽 정리 (0) | 2025.04.27 |
🔧 함수의 타입 선언 – 매개변수, 반환, 옵셔널, 디폴트 완전 정복 (0) | 2025.04.27 |
🧱 객체 타입 선언 – interface vs type 완전 비교 (1) | 2025.04.27 |
댓글