안동민 개발노트 아이콘

안동민 개발노트

2장 : 타입스크립트 기본 타입

특수 타입

앞서 살펴본 기본 타입들은 타입스크립트의 가장 근간이 되는 데이터 형태를 정의했습니다. 이제는 좀 더 유연하고 강력하게 타입을 다룰 수 있도록 돕는 특수 타입들에 대해 알아보겠습니다. 이 특수 타입들은 특정 상황에서 매우 유용하게 활용되며, 타입스크립트의 진정한 강점을 경험하게 해 줄 것입니다.


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의 심화된 사용법까지 알아보았습니다. 특히 unknownstrictNullChecks는 코드의 안전성을 한 단계 더 높여주는 중요한 도구임을 기억해주세요.