any는 타입 검사를 끄는 것이고, unknown은 타입 검사를 ** 미루는** 것이며, never는 ** 존재할 수 없는** 값의 타입입니다.

이 세 가지는 면접에서 거의 반드시 물어보는 주제입니다.

any — 타입 검사의 탈출구

any는 어떤 타입이든 할당 가능하고, 어떤 연산이든 허용합니다.

TYPESCRIPT
let value: any = 42;
value = 'hello';       // OK
value = true;          // OK
value.foo.bar.baz;     // OK — 런타임에 에러가 나더라도 컴파일은 통과
value();               // OK
value[0];              // OK

any는 전염된다

TYPESCRIPT
const data: any = fetchSomething();
const name = data.user.name;  // name: any — any가 전파됨
const upper = name.toUpperCase(); // upper: any

// any가 한 번 들어가면 그 이후 모든 타입 검사가 무의미해짐

any를 써야 할 때

TYPESCRIPT
// 1. JavaScript에서 마이그레이션 중일 때 (임시방편)
// 2. 서드파티 라이브러리에 타입 정의가 없을 때
// 3. 정말로 어떤 타입이든 받아야 하는 유틸리티 함수
//    → 이 경우에도 unknown이 더 좋은 선택

unknown — 안전한 any

unknownany처럼 어떤 타입이든 할당 가능하지만, ** 사용하기 전에 타입을 좁혀야** 합니다.

TYPESCRIPT
let value: unknown = 42;
value = 'hello';       // OK — 할당은 자유
value = true;          // OK

// ❌ 하지만 바로 사용은 불가
// value.toUpperCase(); // Error: Object is of type 'unknown'
// value();             // Error
// value[0];            // Error

// ✅ 타입을 좁힌 후 사용
if (typeof value === 'string') {
  console.log(value.toUpperCase()); // OK
}

unknown의 실전 활용

TYPESCRIPT
// API 응답 처리 — 무엇이 올지 모르는 외부 데이터
async function fetchData(url: string): Promise<unknown> {
  const response = await fetch(url);
  return response.json();
}

// 사용할 때 타입을 검증
const data = await fetchData('/api/user');

// 타입 가드로 검증
function isUser(data: unknown): data is { name: string; age: number } {
  return (
    typeof data === 'object' &&
    data !== null &&
    'name' in data &&
    'age' in data
  );
}

if (isUser(data)) {
  console.log(data.name); // OK — data는 { name: string; age: number }
}

면접 포인트: "any 대신 unknown을 쓰는 이유가 뭔가요?"라고 물어보면, "unknown은 사용 전에 타입 좁히기를 강제하므로 타입 안전성을 유지할 수 있습니다"라고 답하면 됩니다.

never — 존재할 수 없는 값

never는 ** 어떤 값도 가질 수 없는 타입 **입니다. "이 코드는 절대 도달하지 않는다"를 의미합니다.

never가 되는 경우

TYPESCRIPT
// 1. 항상 예외를 던지는 함수
function throwError(message: string): never {
  throw new Error(message);
}

// 2. 무한 루프
function forever(): never {
  while (true) {}
}

// 3. 모든 경우를 처리한 후의 남은 타입
type Shape = 'circle' | 'square';

function getArea(shape: Shape): number {
  switch (shape) {
    case 'circle':
      return Math.PI;
    case 'square':
      return 1;
    default:
      // shape: never — 모든 경우를 이미 처리했으므로
      const _exhaustive: never = shape;
      return _exhaustive;
  }
}

exhaustiveness check (완전성 검사)

never의 가장 실용적인 활용입니다.

TYPESCRIPT
type Animal = 'dog' | 'cat' | 'bird';

function makeSound(animal: Animal): string {
  switch (animal) {
    case 'dog': return '멍멍';
    case 'cat': return '야옹';
    case 'bird': return '짹짹';
    default:
      // 새로운 동물이 추가되면 여기서 컴파일 에러 발생
      const _never: never = animal;
      throw new Error(`처리되지 않은 동물: ${_never}`);
  }
}

// 나중에 Animal에 'fish'를 추가하면
// type Animal = 'dog' | 'cat' | 'bird' | 'fish';
// → default에서 에러: Type 'fish' is not assignable to type 'never'

세 타입의 관계

타입 계층에서의 위치

TYPESCRIPT
// unknown은 모든 타입의 슈퍼타입 (Top Type)
// any는 타입 시스템을 벗어남 (특수한 존재)
// never는 모든 타입의 서브타입 (Bottom Type)

// unknown: 모든 타입이 할당 가능
const a: unknown = 42;
const b: unknown = 'hello';
const c: unknown = true;

// never: 어떤 타입에도 할당 가능 (값이 없으므로 논리적으로 참)
function getDefault(): never {
  throw new Error('no default');
}
const num: number = getDefault(); // OK (never는 number에 할당 가능)
const str: string = getDefault(); // OK (never는 string에 할당 가능)

// 하지만 never에 할당하는 것은 불가
// const n: never = 42; // ❌ Error

연산에서의 동작

TYPESCRIPT
// 유니온에서 never는 사라짐
type A = string | never;   // string
type B = number | never;   // number

// 인터섹션에서 never는 전체를 never로 만듦
type C = string & never;   // never

// 유니온에서 unknown은 전체를 unknown으로 만듦
type D = string | unknown; // unknown

// 인터섹션에서 unknown은 사라짐
type E = string & unknown; // string

any vs unknown vs never 비교

특성anyunknownnever
할당 가능한 값모든 값모든 값없음
다른 타입에 할당가능좁히기 필요가능
연산/접근자유좁히기 필요불가
타입 안전성없음있음완전함
용도임시 탈출구외부 데이터불가능한 상태

실전 팁

TYPESCRIPT
// ❌ any 사용
function parse(json: string): any {
  return JSON.parse(json);
}
const data = parse('{"name": "test"}');
data.whatever.something; // 타입 에러 없이 런타임 에러

// ✅ unknown 사용
function parseSafe(json: string): unknown {
  return JSON.parse(json);
}
const safeDaata = parseSafe('{"name": "test"}');
// safeData.whatever; // ❌ 컴파일 에러 — 타입 좁히기를 강제

정리

  • any는 타입 검사를 끄는 것이므로 가능한 피한다
  • unknown은 안전한 any로, 외부 데이터 처리에 적합하다
  • never는 불가능한 상태를 나타내며, exhaustiveness check에 활용한다
  • 타입 계층: never(바닥) → 각 타입 → unknown(꼭대기)
  • 유니온에서 never는 사라지고, unknown은 전체를 삼킨다
댓글 로딩 중...