본문으로 건너뛰기

안동민 개발노트

본문 시작

특수 타입

unknown·object·void·never·null·undefined의 허용 연산과 할당 관계를 구분해 불확실하거나 반환되지 않는 값을 표현합니다.

앞서 살펴본 기본 타입들은 타입스크립트의 가장 근간이 되는 데이터 형태를 정의했습니다.

이제는 좀 더 유연하고 강력하게 타입을 다룰 수 있도록 돕는 특수 타입들에 대해 알아보겠습니다.

이 특수 타입들은 특정 상황에서 매우 유용하게 활용되며, 타입스크립트의 진정한 강점을 경험하게 해 줄 것입니다.

특수 타입은 “무엇을 아는가”를 정확히 표현한다

이름을 외우기보다 값의 경계, 모양, 종료, 부재 중 어떤 사실을 타입으로 남길지 먼저 묻는다.

  1. 외부 입력은 확인 후 사용

    unknown 아직 값의 형태를 모르는가? unknown → typeof / validator → string 경계 유지: 좁히기 전에는 속성과 메서드를 쓸 수 없다.

  2. 모양은 아직 모른다

    object 원시 값이 아님만 보장하는가? {} [] () => void 속성을 읽으려면 { id: string } 처럼 구조를 더 구체화한다.

  3. 정상 종료와 도달 불가를 구분

    void · never 함수가 어떻게 끝나는가? void: 값 없이 종료 never: 정상 종료 없음 never 는 예외·무한 루프·누락 분기 검사에 쓰인다.

  4. 부재를 유니온에 명시

    null · undefined 값이 없을 수 있는가? string | null number | undefined strictNullChecks 가 사용 전 처리를 강제한다.


unknown (알 수 없음)

unknown은 외부 값을 검증한 뒤에만 쓰게 만든다

any와 달리 unknown은 바로 속성 접근을 허용하지 않아, 타입 가드로 안전 지점을 코드에 남긴다.

  1. Receive API, JSON, form 값

    unknown으로 받음

  2. Guard typeof, Array.isArray

    사용자 가드로 확인

  3. Narrow 검사 블록 안

    구체 타입으로 좁힘

  4. Use 좁힌 뒤에만 속성 접근

    메서드 호출

점검기준
any바로 사용 가능하지만 오류도 함께 통과
unknown검증 전 사용 금지, 검증 지점 명시
판단외부 입력 경계에서는 unknown을 기본값으로 둠

unknown 타입은 any와 비슷해 보이지만, 훨씬 더 타입 안전성(Type Safety)을 강조합니다.

any는 모든 값 할당과 연산을 허용해 타입 검사를 사실상 무력화합니다.

반면 unknown은 값 할당은 허용하되, 값에 대한 연산을 바로 허용하지 않습니다.

unknown 값을 사용하려면 먼저 그 값이 어떤 타입인지 명시적으로 타입 좁히기(Narrowing)를 해야 합니다.

이는 마치 내가 뭘 들고 있는지 모르니 함부로 만지지 마세요!라고 말하는 것과 같습니다.

let value: unknown;

value = 123;         // unknown에 숫자 할당 가능
value = "hello";     // unknown에 문자열 할당 가능
value = true;        // unknown에 불리언 할당 가능
value = { a: 1 };    // unknown에 객체 할당 가능

// 오류: 'value'의 형식이 'unknown'이므로 개체에서 'toUpperCase' 속성을 확인할 수 없습니다.
// value.toUpperCase();

// 오류: 'value'의 형식이 'unknown'이므로 개체에서 'toFixed' 속성을 확인할 수 없습니다.
// value.toFixed(2);

// unknown 타입의 값을 사용하려면 반드시 타입 검사를 통해 타입을 좁혀야 합니다.
if (typeof value === "string") {
  console.log(value.toUpperCase()); // 이제 문자열 메서드를 사용할 수 있습니다.
} else if (typeof value === "number") {
  console.log(value.toFixed(2));    // 이제 숫자 메서드를 사용할 수 있습니다.
} else if (typeof value === "object" && value !== null) {
  // 객체인 경우 추가적인 속성 검사가 필요할 수 있습니다.
  console.log(value);
}

unknown은 API 응답처럼 어떤 형태의 데이터가 올지 확실하지 않을 때 유용합니다.

any보다 안전하게 미지의 데이터를 다룰 수 있도록 돕기 때문에, 가능한 한 any 대신 unknown을 사용하는 것이 좋습니다.


object (객체)

object 타입은 원시 타입(primitive types: number, string, boolean, symbol, null, undefined)이 아닌 모든 타입을 나타냅니다.

즉, 객체, 배열, 함수 등을 포함하는 더 넓은 개념의 "객체"를 의미합니다.

let obj: object;

obj = { name: "Alice", age: 30 }; // 객체
obj = [1, 2, 3];                   // 배열도 객체의 일종
obj = function() { console.log("Hello"); }; // 함수도 객체의 일종

// 오류: 원시 타입은 object 타입에 할당될 수 없습니다.
// obj = 100;
// obj = "hello";
// obj = true;

// object 타입은 객체의 특정 속성에 바로 접근하는 것을 허용하지 않습니다.
// 오류: 'obj'의 형식이 'object'이므로 개체에서 'name' 속성을 확인할 수 없습니다.
// console.log(obj.name);

object 타입은 매우 일반적인 타입이므로, 실제 코딩에서는 특정 객체 구조를 정의하는 객체 리터럴 타입(예: { name: string, age: number })이나 인터페이스(Interface), 타입 별칭(Type Alias)을 더 자주 사용하게 됩니다.

object 타입은 단순히 이 값은 원시 타입이 아니다라고 명시할 때 유용합니다.


voidnever의 재조명 (함수 관점)

기본 타입에서 이미 voidnever를 다루었지만, 이 두 타입은 특히 함수의 반환 타입과 관련하여 중요한 의미를 가지므로 다시 한번 강조하고자 합니다.

  • void: 함수가 아무런 값도 반환하지 않을 때 사용됩니다.

    console.log()처럼 단순히 작업을 수행하고 끝나는 함수가 대표적입니다.

    undefined를 반환하는 함수도 void 타입으로 간주될 수 있습니다.

    function logMessage(message: string): void {
      console.log(message);
      // return undefined; // 명시적으로 undefined를 반환해도 void입니다.
      // return;          // 이것도 void
    }
    
    logMessage("반환값이 없는 함수입니다.");
    // let result: string = logMessage("테스트"); // 오류: 'void' 형식은 'string' 형식에 할당될 수 없습니다.
  • never: 함수가 절대로 값을 반환하지 않거나, 정상적으로 종료되지 않을 때 사용됩니다.

    이는 함수가 항상 예외를 던지거나, 무한 루프에 빠지는 경우를 의미합니다.

    즉, 함수의 실행 흐름이 끝에 도달할 수 없음을 나타냅니다.

    // 항상 예외를 발생시키는 함수
    function throwError(message: string): never {
      throw new Error(message);
    }
    
    // 무한 루프에 빠지는 함수
    function keepProcessing(): never {
      while (true) {
        // ... 계속해서 작업을 수행
      }
    }
    
    // 이 함수는 never 타입을 반환하므로, 정상적인 값을 할당받을 수 없습니다.
    // let result: string = throwError("치명적인 오류 발생!"); // 오류: 'never' 형식은 'string' 형식에 할당될 수 없습니다.

    voidnever의 가장 큰 차이점은 void는 함수가 undefined를 반환할 수 있지만 (명시적 또는 묵시적으로), never는 함수가 어떤 방식으로든 결코 정상적으로 종료되지 않는다는 것을 의미합니다.


nullundefined의 특수성

다시 한번 nullundefined를 언급하는 이유는, 이들이 타입스크립트의 strictNullChecks 컴파일 옵션과 결합될 때 가지는 중요한 의미 때문입니다.

  • strictNullChecks: false (기본값 또는 레거시 설정): 이 설정에서는 nullundefined가 모든 다른 타입의 하위 타입으로 간주됩니다. 즉, number 타입 변수에 null이나 undefined를 할당하는 것이 허용됩니다. 이는 자바스크립트의 느슨한 특성을 반영하지만, 런타임에 TypeError: Cannot read property of null (or undefined) 같은 흔한 오류를 유발할 수 있습니다.

    // tsconfig.json 에 "strictNullChecks": false 일 때
    let myNumber: number = 10;
    myNumber = null;      // 허용됨
    myNumber = undefined; // 허용됨
    console.log(myNumber); // undefined
  • strictNullChecks: true (권장 설정): 이 설정을 활성화하면, nullundefined는 오직 any 타입이나, 자신의 타입(즉, nullnull에만, undefinedundefined에만), 그리고 void 타입에만 할당될 수 있습니다.

    다른 타입에 null 또는 undefined를 할당하려고 하면 컴파일 시 오류가 발생합니다.

    // tsconfig.json 에 "strictNullChecks": true 일 때
    let myNumber: number = 10;
    // myNumber = null;      // 오류: 'null' 형식은 'number' 형식에 할당될 수 없습니다.
    // myNumber = undefined; // 오류: 'undefined' 형식은 'number' 형식에 할당될 수 없습니다.
    
    // 만약 null 또는 undefined를 허용하고 싶다면, 유니온 타입을 사용해야 합니다.
    let nullableString: string | null = "Hello";
    nullableString = null; // 허용됨
    
    let optionalNumber: number | undefined = 50;
    optionalNumber = undefined; // 허용됨

    이 책에서는 strictNullChecks: true를 활성화하는 것을 강력히 권장합니다.

    이 설정을 통해 타입스크립트의 안전성을 극대화하고, 런타임에 발생할 수 있는 null 관련 오류를 사전에 방지할 수 있기 때문입니다.

    초기 설정 시 tsconfig.json 파일에서 이 옵션을 true로 변경해두시는 것이 좋습니다.


아래 다이어그램은 지금까지 살펴본 특수 타입들을 실전 판단 기준으로 다시 묶어 정리한 것입니다.

특수 타입은 값보다 사용 가능한 범위와 흐름을 설명한다

unknown, object, void, never, null, undefined는 값의 모양보다 “어디까지 쓸 수 있는가”를 드러낸다.

  1. unknown
    unknown 외부 값 가드 후

    사용

  2. object
    object 넓

    객체 구체 구조 타입을 우선

  3. void
    void 반환값 사용 안 함

    작업 수행 함수

  4. never
    never 정상 복귀 없음 throw

    무한 루프, 불가능 분기

상황판단
null/undefined값 없음 자체를 모델링할 때만 명시
피할 것any로 열어두고 바로 속성 접근
핵심타입은 가능한 사용 범위를 줄이는 장치

특수 타입은 강력하지만, 잘못 쓰면 타입 검사가 느슨해지거나 흐름 의미가 흐려질 수 있습니다.

아래 다이어그램은 unknown, object, void, never, null, undefined를 선택할 때의 안전한 처리 순서를 정리한 것입니다.

특수 타입 안전 사용 흐름

unknown과 object는 입력 경계를 넓게 받되, 실제 연산 전에는 반드시 검사로 의미를 좁혀야 합니다.

  1. unknown
    외부 입력을 안전하게 받기

    API 응답이나 JSON처럼 모양을 모르는 값은 바로 쓰지 말고 타입 가드 뒤에 사용합니다.

  2. 2
    함수의 종료 의미 표시

    반환값이 없으면 void, 정상 종료가 불가능하면 never로 흐름 자체를 문서화합니다.

  3. 3
    값 없음의 가능성 노출

    strictNullChecks를 켜고, 값이 없을 수 있는 곳은 유니온으로 명시합니다.


실전에서는 특수 타입을 단독 문법으로 외우기보다, 값이 들어오고 검증되고 사용되는 흐름 속에서 어디에 배치할지 판단해야 합니다.

특수 타입은 값의 안전 단계에 배치한다

입력의 불확실성을 `unknown`으로 받고, 확인 후 도메인 타입으로 옮기며, 불가능한 흐름은 `never`로 닫습니다.

  1. 1
    외부 입력

    API, JSON, 폼 값은 처음부터 신뢰하지 않습니다.

  2. 2
    unknown

    값은 담되 바로 읽지 못하게 막아 둡니다.

  3. 3
    가드와 검증

    런타임에서 확인 가능한 조건으로 좁힙니다.

  4. 4
    도메인 타입

    검증된 값만 실제 비즈니스 로직에 넘깁니다.

  5. 5
    never

    도달하면 안 되는 분기를 컴파일러가 확인하게 합니다.


이렇게 unknown, object, 그리고 void, never, null, undefined의 심화된 사용법까지 알아보았습니다.

특히 unknownstrictNullChecks는 코드의 안전성을 한 단계 더 높여주는 중요한 도구임을 기억해주세요.

특수 타입 안전 사용 지도

unknown, object, void, never, null, undefined는 빈칸이 아니라 의도를 표현하는 타입이다.

  1. 검증 필요
    unknown

    외부에서 온 값은 먼저 unknown으로 받고 좁힌 뒤 사용한다.

  2. 넓은 객체
    object

    원시값이 아닌 구조만 받는 계약에 쓰되 구체 속성은 따로 정의한다.

  3. 제어 흐름
    void / never

    콜백 반환 무시는 void, 절대 돌아오지 않는 흐름은 never로 둔다.

  4. 부재 표현
    null / undefined

    값 없음의 의미를 하나로 정하고 optional과 초기화 규칙을 맞춘다.

특수 타입를 코드에 적용하기 전, 컴파일 오류가 막아 줄 지점과 사람이 약속해야 할 지점을 나눕니다.

특수 타입의 값 흐름

unknown, object, void, never, null, 누락값은 단순 값 종류보다 값의 확실성이나 함수 흐름의 상태를 설명할 때 쓰입니다.

  1. 1
    확인 전 값

    unknown 외부 입력처럼 아직 타입을 믿을 수 없는 값은 검사 후에만 구체 타입으로 사용합니다. typeof 점검

  2. 2
    구조가 있는 값

    object 원시값이 아닌 객체 영역을 다룰 때 쓰되 세부 속성은 인터페이스로 더 좁힙니다. object

  3. 3
    반환값 없음

    void 로그 출력이나 상태 변경처럼 결과 값을 사용하지 않는 함수의 반환을 표시합니다. (): void

  4. 4
    도달 불가

    never 예외를 던지거나 모든 케이스를 처리한 뒤 남지 않는 분기를 표현합니다. exhaustive

아래 다이어그램은 unknown, object, void, never, null, undefined를 안전성 기준으로 나눠 봅니다.

특수 타입 안전성 구분

특수 타입은 예외적인 문법이 아니라 값의 불확실성, 반환 흐름, 값 없음의 의미를 분리하기 위한 장치입니다.

  1. unknown
    확인 전 사용 금지

    unknown 외부 입력처럼 타입을 모르는 값은 검사 뒤에야 메서드와 속성 접근 범위가 열린다. typeof value

  2. object
    원시값 제외 객체

    object 객체임은 알지만 구체 속성은 모를 때 쓰며 속성 접근에는 추가 타입이 필요합니다. Record<string, unknown>

  3. void와 never
    반환 없음과 불가능

    void와 never void는 반환값을 쓰지 않는 함수, never는 정상 종료 자체가 없는 흐름입니다. throw new Error()

  4. null 경계
    strictNullChecks

    null 경계 null과 undefined를 별도 값으로 다루면 누락된 상태를 더 정확히 처리합니다. value ?? fallback

아래 다이어그램은 특수 타입을 실제 코드에 적용하기 전에 값의 모양, 타입 선언, 오류 확인 순서를 점검합니다.