빠른 비교
const raw: unknown = JSON.parse("{}");
if (typeof raw === "object" && raw !== null) {
console.log(raw);
}
function fail(message: string): never {
throw new Error(message);
}타입 경계
먼저 자주 보는 기본형은 아래 세 가지입니다.
let value: anyconst raw: unknown = ...function fail(...): nevercatch (err: unknown)const _exhaustive: never = value
1) any는 타입 검사를 우회하는 탈출구다
any를 쓰면 거의 모든 속성 접근과 호출이 허용됩니다.
그래서 일시적으로는 편하지만, 그 지점부터 타입 정보가 연쇄적으로 약해지기 쉽습니다.
let value: any = "hello";
value.notARealMethod();정말 마이그레이션 중 임시 우회가 아니라면, any는 가능한 한 좁은 범위에만 남기는 편이 좋습니다.
2) unknown은 외부 입력에 더 정직하다
값의 실제 타입을 아직 모르지만, 사용 전에 확인하겠다는 뜻이라면 unknown이 더 맞습니다.
JSON 파싱 결과, 사용자 입력, catch 값처럼 불확실한 데이터에 특히 잘 맞습니다.
const raw: unknown = JSON.parse(input);
if (typeof raw === "string") {
console.log(raw.toUpperCase());
}즉 unknown은 느슨한 타입이 아니라 확인 절차를 강제하는 타입입니다.
try {
riskyWork();
} catch (err: unknown) {
if (err instanceof Error) {
console.error(err.message);
}
}const unsafe: any = JSON.parse(input);
const safe: unknown = JSON.parse(input);3) never는 불가능한 상태를 드러낸다
never는 값이 절대 존재하지 않는 경우에 등장합니다.
항상 throw하는 함수, 모든 union case를 처리한 뒤 남는 불가능한 분기에서 자주 봅니다.
function fail(message: string): never {
throw new Error(message);
}4) exhaustiveness 체크에서 never가 특히 중요하다
union의 모든 경우를 처리했다고 가정하는 switch 문 끝에서 never를 사용하면, 새 케이스가 추가됐을 때 누락을 컴파일 단계에서 드러낼 수 있습니다.
type Shape =
| { kind: "circle"; r: number }
| { kind: "rect"; w: number; h: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.r ** 2;
case "rect":
return shape.w * shape.h;
default: {
const _exhaustive: never = shape;
return _exhaustive;
}
}
}이 패턴은 "여기까지 왔다면 새 케이스를 빠뜨린 것"이라는 신호를 타입에 남기는 방식입니다.
5) 장기 유지보수에서는 unknown이 기본값에 가깝다
모르는 값을 받는 순간 습관적으로 any를 붙이면 타입 시스템이 약해집니다.
장기적으로는 unknown으로 받아 narrowing하는 쪽이 더 안전하고 예측 가능합니다.
언제 무엇을 쓸까
체크포인트
- 타입 검사를 잠시 우회한다:
any - 외부 입력처럼 아직 모르는 값이다:
unknown - 불가능한 분기다:
never - union 누락을 검출한다:
neverexhaustiveness check - 장기 유지보수가 중요하다:
unknown우선
주의할 점
unknown 대신 습관적으로 any를 쓰면 TypeScript의 장점이 빠르게 사라집니다.
// ❌ 외부 입력을 바로 any로 둠
const data: any = JSON.parse(input);
console.log(data.user.name.toUpperCase());
// ✅ unknown으로 받고 확인 후 사용
const data: unknown = JSON.parse(input);
if (typeof data === "object" && data !== null && "user" in data) {
console.log(data);
}
// ✅ never는 도달하면 안 되는 분기를 표시할 때도 쓴다
function fail(message: string): never {
throw new Error(message);
}참고 링크
2 sources