노트

any와 unknown

any and unknown

프런트엔드#ts · 연결된 개념 8개

쉽게 말하면

any와 unknown은 둘 다 뭐가 들었는지 모르는 상자예요. any는 확인 없이 바로 꺼내 쓰다가 나중에 터지고, unknown은 열어서 뭔지 확인해야만 꺼내 쓸 수 있게 막아 줘요.

비유가 깨지는 곳 any는 상자 하나로 끝나지 않아요. any에서 꺼낸 값도 any라서 검사 없는 상태가 코드 전체로 번져요. 그래서 외부에서 들어온 값은 unknown으로 받고 타입 가드로 좁혀요.

any는 그 값에 대한 타입 검사를 포기하는 타입이고, unknown은 타입을 아직 모르니 확인하기 전에는 쓰지 못하게 하는 타입이다. 둘 다 아무 값이나 받을 수 있지만, 꺼내 쓸 때가 다르다.

let a: any = 'hi'
a.foo.bar()            // 통과 → 런타임에 터짐
const n: number = a    // 통과
 
let u: unknown = 'hi'
u.toUpperCase()        // ❌ 먼저 좁혀야 함
if (typeof u === 'string') u.toUpperCase() // ✅
  • unknown은 TS 3.0에서 들어왔다. JSON.parse·fetch().json()·postMessage·localStorage처럼 외부에서 들어오는 값은 unknown으로 받고 타입 가드나 Zod 같은 런타임 스키마(Runtime Schema)로 좁힌다
  • catch (e)의 e는 무엇이든 throw될 수 있으니 unknown이 맞다. useUnknownInCatchVariables(strict에 포함)가 이를 강제한다. e instanceof Error로 확인하고 쓴다
  • 제네릭의 기본값도 T = any보다 T = unknown이 안전하다
  • any는 퍼진다. any에서 꺼낸 값도 any라 오류가 숨겨진 채 코드 전체로 번진다. 정말 타입을 알 수 없는 서드파티 경계에서만 좁게 쓴다
  • never는 반대편 끝이다. 어떤 값도 될 수 없는 타입으로, 끝까지 좁혀진 분기나 절대 반환하지 않는 함수에 나온다(판별 유니언)

검증되지 않은 값을 검증된 값과 타입으로 구분하는 방법은 타입 브랜딩, 신뢰 경계에서의 입력 검증은 Bean Validation·프로토타입 오염과 같은 문제다.

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 매핑된 타입과 keyof

    매핑된 타입(Mapped Types)은 기존 타입의 키를 하나씩 돌며 새 타입을 만드는 문법([K in keyof T]: …)이다. keyof T는 객체 타입의 키 유니언을, T[K](인덱스 접근 타입, Indexed Access Type)는 그 키의 값 타입을 꺼낸다.

  • type과 interface

    type(타입 별칭, Type Alias)과 interface는 둘 다 타입에 이름을 붙이는 방법이다. 객체 모양을 정의할 때는 대부분 바꿔 써도 되지만, 확장 방식과 표현 범위가 다르다.

  • 제네릭

    제네릭(Generics)은 타입을 매개변수처럼 받아, 여러 타입에 같은 로직을 재사용하면서도 타입 정보를 잃지 않게 하는 기능이다.

  • enum과 as const 객체

    TypeScript의 enum은 관련된 상수 집합에 이름과 타입을 함께 주는 문법이다. 다른 TS 타입과 달리 런타임 객체로 컴파일되어 남는다. 요즘 프런트엔드에서는 같은 일을 as const 객체와 유니언 타입으로 하는 경우가 많다.

  • 유니언·인터섹션·리터럴 타입

    유니언 타입(Union Type, A | B)은 "A이거나 B인 값"이고, 인터섹션 타입(Intersection Type, A & B)은 "A이면서 B인 값"이다. 리터럴 타입(Literal Type)은 'left'나 3처럼 특정 값 하나만 허용하는 타입이다.

보기 옵션