특수 타입
unknown·object·void·never·null·undefined의 허용 연산과 할당 관계를 구분해 불확실하거나 반환되지 않는 값을 표현합니다.
앞서 살펴본 기본 타입들은 타입스크립트의 가장 근간이 되는 데이터 형태를 정의했습니다.
이제는 좀 더 유연하고 강력하게 타입을 다룰 수 있도록 돕는 특수 타입들에 대해 알아보겠습니다.
이 특수 타입들은 특정 상황에서 매우 유용하게 활용되며, 타입스크립트의 진정한 강점을 경험하게 해 줄 것입니다.
이름을 외우기보다 값의 경계, 모양, 종료, 부재 중 어떤 사실을 타입으로 남길지 먼저 묻는다.
- 외부 입력은 확인 후 사용
unknown 아직 값의 형태를 모르는가? unknown → typeof / validator → string 경계 유지: 좁히기 전에는 속성과 메서드를 쓸 수 없다.
- 모양은 아직 모른다
object 원시 값이 아님만 보장하는가? {} [] () => void 속성을 읽으려면 { id: string } 처럼 구조를 더 구체화한다.
- 정상 종료와 도달 불가를 구분
void · never 함수가 어떻게 끝나는가? void: 값 없이 종료 never: 정상 종료 없음 never 는 예외·무한 루프·누락 분기 검사에 쓰인다.
- 부재를 유니온에 명시
null · undefined 값이 없을 수 있는가? string | null number | undefined strictNullChecks 가 사용 전 처리를 강제한다.
unknown (알 수 없음)
any와 달리 unknown은 바로 속성 접근을 허용하지 않아, 타입 가드로 안전 지점을 코드에 남긴다.
- Receive API, JSON, form 값
unknown으로 받음
- Guard typeof, Array.isArray
사용자 가드로 확인
- Narrow 검사 블록 안
구체 타입으로 좁힘
- 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 타입은 단순히 이 값은 원시 타입이 아니다라고 명시할 때 유용합니다.
void와 never의 재조명 (함수 관점)
기본 타입에서 이미 void와 never를 다루었지만, 이 두 타입은 특히 함수의 반환 타입과 관련하여 중요한 의미를 가지므로 다시 한번 강조하고자 합니다.
-
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' 형식에 할당될 수 없습니다.void와never의 가장 큰 차이점은void는 함수가undefined를 반환할 수 있지만 (명시적 또는 묵시적으로),never는 함수가 어떤 방식으로든 결코 정상적으로 종료되지 않는다는 것을 의미합니다.
null과 undefined의 특수성
다시 한번 null과 undefined를 언급하는 이유는, 이들이 타입스크립트의 strictNullChecks 컴파일 옵션과 결합될 때 가지는 중요한 의미 때문입니다.
-
strictNullChecks: false(기본값 또는 레거시 설정): 이 설정에서는null과undefined가 모든 다른 타입의 하위 타입으로 간주됩니다. 즉,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(권장 설정): 이 설정을 활성화하면,null과undefined는 오직any타입이나, 자신의 타입(즉,null은null에만,undefined는undefined에만), 그리고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는 값의 모양보다 “어디까지 쓸 수 있는가”를 드러낸다.
- unknownunknown 외부 값 가드 후
사용
- objectobject 넓
객체 구체 구조 타입을 우선
- voidvoid 반환값 사용 안 함
작업 수행 함수
- nevernever 정상 복귀 없음 throw
무한 루프, 불가능 분기
| 상황 | 판단 |
|---|---|
| null/undefined | 값 없음 자체를 모델링할 때만 명시 |
| 피할 것 | any로 열어두고 바로 속성 접근 |
| 핵심 | 타입은 가능한 사용 범위를 줄이는 장치 |
특수 타입은 강력하지만, 잘못 쓰면 타입 검사가 느슨해지거나 흐름 의미가 흐려질 수 있습니다.
아래 다이어그램은 unknown, object, void, never, null, undefined를 선택할 때의 안전한 처리 순서를 정리한 것입니다.
unknown과 object는 입력 경계를 넓게 받되, 실제 연산 전에는 반드시 검사로 의미를 좁혀야 합니다.
- unknown외부 입력을 안전하게 받기
API 응답이나 JSON처럼 모양을 모르는 값은 바로 쓰지 말고 타입 가드 뒤에 사용합니다.
- 2함수의 종료 의미 표시
반환값이 없으면 void, 정상 종료가 불가능하면 never로 흐름 자체를 문서화합니다.
- 3값 없음의 가능성 노출
strictNullChecks를 켜고, 값이 없을 수 있는 곳은 유니온으로 명시합니다.
실전에서는 특수 타입을 단독 문법으로 외우기보다, 값이 들어오고 검증되고 사용되는 흐름 속에서 어디에 배치할지 판단해야 합니다.
입력의 불확실성을 `unknown`으로 받고, 확인 후 도메인 타입으로 옮기며, 불가능한 흐름은 `never`로 닫습니다.
- 1외부 입력
API, JSON, 폼 값은 처음부터 신뢰하지 않습니다.
- 2unknown
값은 담되 바로 읽지 못하게 막아 둡니다.
- 3가드와 검증
런타임에서 확인 가능한 조건으로 좁힙니다.
- 4도메인 타입
검증된 값만 실제 비즈니스 로직에 넘깁니다.
- 5never
도달하면 안 되는 분기를 컴파일러가 확인하게 합니다.
이렇게 unknown, object, 그리고 void, never, null, undefined의 심화된 사용법까지 알아보았습니다.
특히 unknown과 strictNullChecks는 코드의 안전성을 한 단계 더 높여주는 중요한 도구임을 기억해주세요.
특수 타입를 코드에 적용하기 전, 컴파일 오류가 막아 줄 지점과 사람이 약속해야 할 지점을 나눕니다.
unknown, object, void, never, null, 누락값은 단순 값 종류보다 값의 확실성이나 함수 흐름의 상태를 설명할 때 쓰입니다.
- 1확인 전 값
unknown 외부 입력처럼 아직 타입을 믿을 수 없는 값은 검사 후에만 구체 타입으로 사용합니다. typeof 점검
- 2구조가 있는 값
object 원시값이 아닌 객체 영역을 다룰 때 쓰되 세부 속성은 인터페이스로 더 좁힙니다. object
- 3반환값 없음
void 로그 출력이나 상태 변경처럼 결과 값을 사용하지 않는 함수의 반환을 표시합니다. (): void
- 4도달 불가
never 예외를 던지거나 모든 케이스를 처리한 뒤 남지 않는 분기를 표현합니다. exhaustive
아래 다이어그램은 unknown, object, void, never, null, undefined를 안전성 기준으로 나눠 봅니다.
특수 타입은 예외적인 문법이 아니라 값의 불확실성, 반환 흐름, 값 없음의 의미를 분리하기 위한 장치입니다.
- unknown확인 전 사용 금지
unknown 외부 입력처럼 타입을 모르는 값은 검사 뒤에야 메서드와 속성 접근 범위가 열린다. typeof value
- object원시값 제외 객체
object 객체임은 알지만 구체 속성은 모를 때 쓰며 속성 접근에는 추가 타입이 필요합니다. Record<string, unknown>
- void와 never반환 없음과 불가능
void와 never void는 반환값을 쓰지 않는 함수, never는 정상 종료 자체가 없는 흐름입니다. throw new Error()
- null 경계strictNullChecks
null 경계 null과 undefined를 별도 값으로 다루면 누락된 상태를 더 정확히 처리합니다. value ?? fallback
아래 다이어그램은 특수 타입을 실제 코드에 적용하기 전에 값의 모양, 타입 선언, 오류 확인 순서를 점검합니다.