icon
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는 코드의 안전성을 한 단계 더 높여주는 중요한 도구임을 기억해주세요.