TypeScript에서 any, unknown, never의 차이점을 설명해주세요.
답변 포인트
타입 안전성, 사용 전 타입 좁히기, 도달 불가능한 값이라는 관점에서 비교해보세요.
정답 및 해설
빠른 요약
any는 타입 검사를 사실상 끄는 타입으로 어떤 값이든 할당하고 사용할 수 있지만 타입 안전성이 낮습니다. unknown은 어떤 값이든 받을 수 있지만 사용하기 전 타입 가드로 좁혀야 하므로 any보다 안전합니다.
any, unknown, never는 TypeScript의 타입 시스템을 이해할 때 중요한 특수 타입입니다. 셋 모두 "일반적인 값 타입"과는 다르게 동작하며, 각각 안전성 수준과 의미가 다릅니다.
any
any는 타입 검사를 사실상 끄는 타입입니다. 어떤 값이든 할당할 수 있고, 어떤 연산이든 허용됩니다.
let value: any = "hello";
value.toUpperCase();
value.notExists();
value = 123;
value();컴파일러가 오류를 잡아주지 않으므로 빠르게 마이그레이션할 때는 유용하지만, 실무 코드에서는 타입 안전성을 크게 떨어뜨립니다.
unknown
unknown은 "무엇인지 아직 모르는 값"을 표현합니다. 어떤 값이든 할당할 수 있지만, 사용하기 전에는 타입을 좁혀야 합니다.
let value: unknown = "hello";
// value.toUpperCase(); // 오류
if (typeof value === "string") {
value.toUpperCase(); // OK
}외부 입력처럼 타입을 신뢰할 수 없는 값에는 any보다 unknown이 안전합니다.
function parseJson(json: string): unknown {
return JSON.parse(json);
}
const result = parseJson('{"name":"Kim"}');
if (
typeof result === "object" &&
result !== null &&
"name" in result
) {
console.log(result.name);
}never
never는 절대 발생할 수 없는 값을 의미합니다. 함수가 정상적으로 반환하지 않거나, 모든 경우를 처리한 뒤 남는 타입에 사용됩니다.
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}exhaustiveness check
never는 유니온 타입의 모든 케이스를 처리했는지 검증할 때 자주 사용됩니다.
type Status = "idle" | "loading" | "success" | "error";
function render(status: Status) {
switch (status) {
case "idle":
return "대기";
case "loading":
return "로딩";
case "success":
return "성공";
case "error":
return "실패";
default: {
const exhaustiveCheck: never = status;
return exhaustiveCheck;
}
}
}나중에 Status에 "cancelled"가 추가되면 default에서 컴파일 오류가 발생합니다.
비교
| 타입 | 의미 | 사용 가능성 | 안전성 |
|---|---|---|---|
| any | 아무 타입이나 허용 | 바로 사용 가능 | 낮음 |
| unknown | 아직 모르는 타입 | 좁힌 후 사용 가능 | 높음 |
| never | 존재할 수 없는 값 | 값 생성 불가 | exhaustive check에 유용 |
정리
외부 데이터나 불확실한 값에는 unknown을 우선 사용하고, any는 마이그레이션이나 타입 표현이 어려운 임시 구간에 제한적으로 사용하는 것이 좋습니다. never는 도달 불가능한 코드와 모든 케이스 처리 검증에 활용합니다.
실무에서의 선택 기준
any, unknown, never는 모두 “특수한 타입”이지만 의미가 완전히 다릅니다. any는 타입 검사를 포기하는 탈출구, unknown은 안전하게 좁혀야 하는 알 수 없는 값, never는 절대 발생하지 않는 값입니다.
function parseJsonUnsafe(json: string): any {
return JSON.parse(json); // 이후 모든 접근이 허용됨
}
function parseJsonSafe(json: string): unknown {
return JSON.parse(json); // 사용 전 검증 필요
}
const value = parseJsonSafe('{"name":"Kim"}');
if (typeof value === 'object' && value !== null && 'name' in value) {
console.log(value.name); // 좁히기 후 접근
}never가 빛나는 상황
type Status = 'idle' | 'loading' | 'success' | 'error';
function assertNever(x: never): never {
throw new Error(`Unexpected value: ${x}`);
}
function label(status: Status) {
switch (status) {
case 'idle': return '대기';
case 'loading': return '로딩';
case 'success': return '성공';
case 'error': return '실패';
default: return assertNever(status);
}
}새 상태가 추가되면 default에서 컴파일 에러가 나므로 누락을 빨리 발견할 수 있습니다.
정리 기준
| 타입 | 의미 | 권장 사용 |
|---|---|---|
any | 타입 검사를 우회 | 레거시/마이그레이션 중 최소화 |
unknown | 아직 모르는 값 | 외부 입력, JSON, 에러 객체 |
never | 도달 불가능 | exhaustive check, 불가능한 분기 |