타입스크립트의 타입은 값들의 집합으로 이해할 수 있다.
타입 계층도
타입스크립트의 모든 타입은 계층 구조를 이루고 있다.

최상위에는 unknown 타입(전체 집합)이 있고, 최하위에는 never타입(공집합)이 있으며, 그 사이에 다양한 타입들이 위치한다.
슈퍼타입과 서브타입
- 슈퍼타입(부모 타입): 더 넓은 범위의 값을 포함하는 타입
- 서브타입(자식 타입): 더 좁은 범위의 값을 포함하는 타입
예를 들어,

number 타입은 모든 숫자 값을 포함하고

20이라는 number 리터럴 타입은 오직 20만 포함한다.

따라서 number 리터럴 타입은 number 타입의 부분집합(서브타입)이다.
타입 호환성
타입 호환성이란 A 타입의 값을 B 타입으로 취급해도 괜찮은지 판단하는 것이다.

업캐스팅 (Up Cast)
서브타입의 값을 슈퍼타입으로 취급하는 것은 항상 가능하다.
let num1: nubmer = 10;
let num2: 10 = 10;
num1 = num2; // ✅ 정상: 업캐스팅

10은 number에 포함되므로 항상 할당할 수 있다.
다운캐스팅 (Down Cast)
슈퍼타입의 값을 서브타입으로 취급하는 것을 불가능하다.
let num1: number = 10;
let num2: 10 = 10;
num2 = num1; // ❌ 오류: 다운캐스팅 불가

num1은 number 타입이므로 10 외에 다른 숫자로 가질 수 있다. 따라서 10만 허용하는 num2에 할당할 수 없다.


기본 타입의 호환성
unknown 타입 (전체 집합)
unknown은 모든 타입의 슈퍼타입이다. 타입 계층도의 최상위에 위치하며, 모든 값을 포함할 수 있다.

따라서 모든 타입은 unknown 타입으로 업캐스트 할 수 있다.
function unknownExam() {
let a: unknown = 1;
let b: unknown = "hello";
let c: unknown = true;
let d: unknown = null;
let e: unknown = undefined;
}
하지만 unknown 타입의 값은 다른 타입으로 다운캐스트할 수 없다.

never 타입 (공집합)
never는 모든 타입의 서브타입이다.

공집합은 모든 집합의 부분집합이므로, never 타입은 모든 타입으로 업캐스트할 수 있다.
function neverExam() {
function neverFunc(): never {
while (true) {}
}
let num: number = neverFunc(); // ✅ 정상
let str: string = neverFunc(); // ✅ 정상
let bool: boolean = neverFunc(); // ✅ 정상
}
하지만 never는 순수한 공집합이므로, 그 어떤 타입도 never로 다운캐스트할 수 없다. (any 타입도 예외는 아니다)

void 타입
void 타입은 undefined의 슈퍼타입이다.

따라서 반환값을 void로 선언한 함수에서 undefined를 반환해도 오류가 발생하지 않는다.
function voidExam() {
function voidFunc(): void {
console.log("hi");
return undefined; // ✅ 정상
}
let voidVar: void = undefined; // ✅ 정상
}
any 타입 (치트키)
any 타입은 타입 계층도를 무시하는 특별한 타입이다.

모든 타입의 슈퍼타입이 될 수도 있고, 모든 타입의 서브타입이 될 수도 있다.
function anyExam() {
let unknownVar: unknown;
let anyVar: any;
let undefinedVar: undefined;
// unknown 타입이 any 타입으로 다운캐스트 가능
anyVar = unknownVar; // ✅ 정상
// any 타입이 undefined로 다운캐스트 가능
undefinedVar = anyVar; // ✅ 정상
}
단, never 타입만은 예외이다.
never는 순수한 공집합이기 때문에 any 타입조차도 never로 다운캐스트할 수 없다.

기본 타입 호환성 표

객체 타입의 호환성
모든 객체 타입은 각각 다른 객체 타입들과 슈퍼-서브 타입의 관계를 갖는다. 따라서 업캐스팅은 허용하고 다운캐스팅은 허용하지 않는다.
예제 1: Animal과 Dog
type Animal = {
name: string;
color: string;
};
type Dog = {
name: string;
color: string;
breed: string;
};
let animal: Animal = {
name: "기린",
color: "yellow",
};
let dog: Dog = {
name: "와와",
color: "brown",
breed: "치와와",
};
animal = dog; // ✅ 정상: Dog → Animal (업캐스팅)
dog = animal; // ❌ 오류: Animal → Dog (다운캐스팅)

Dog 타입은 Animal 타입의 모든 프로퍼티를 포함하면서 breed 프로퍼티를 추가로 가지므로, Dog는 Animal의 서브타입이다.
따라서 Animal 타입 변수에 Dog 타입 값을 할당하는 것은 가능하지만 그 반대는 불가능하다.
예제 2: Book과 ProgrammingBook
type Book = {
name: string;
price: number;
};
type ProgrammingBook = {
name: string;
price: number;
skill: string;
};
let book: Book;
let programmingBook: ProgrammingBook = {
name: "한 입 크기로 잘라먹는 타입스크립트",
price: 33000,
skill: "TypeScript",
};
book = programmingBook; // ✅ 정상: ProgrammingBook → Book (업캐스팅)
programmingBook = book; // ❌ 오류: Book → ProgrammingBook (다운캐스팅)

초과 프로퍼티 검사
초과 프로퍼티 검사란 변수를 객체 리터럴로 초기화할 때 발동하는 타입스크립트의 특수한 기능이다.
이 기능은 타입에 정의된 프로퍼티 외의 다른 초과된 프로퍼티를 가지는 객체는 변수에 할당할 수 없도록 막는다.
type Book = {
name: string;
price: number;
};
let book2: Book = { // ❌ 오류 발생
name: "한 입 크기로 잘라먹는 리액트",
price: 33000,
skill: "ReactJS", // 초과 프로퍼티
};

함수의 인자로 전달할 때도 마찬가지이다.
function func(book: Book) {}
func({ // ❌ 오류 발생
name: "한 입 크기로 잘라먹는 리액트",
price: 33000,
skill: "ReactJS", // 초과 프로퍼티
});

위 코드는 Book 타입에 정의되지 않은 skill 프로퍼티를 갖는 객체를 할당하려고 했으므로 초과 프로퍼티 검사에 의해 오류가 발생한다.
초과 프로퍼티 검사 우회하기
이러한 초과 프로퍼티 검사는 객체 리터럴을 직접 사용하지 않으면 발생하지 않는다.
값을 별도의 변수에 보관한 다음 변수 값을 사용하면 검사가 발생하지 않는다.
let programmingBook: ProgrammingBook = {
name: "한 입 크기로 잘라먹는 타입스크립트",
price: 33000,
skill: "TypeScript",
};
let book3: Book = programmingBook; // ✅ 정상
func(programmingBook); // ✅ 정상
이 경우 일반적인 타입 호환성 규칙(업캐스팅)이 적용되어 오류가 발생하지 않는다.
'Frontend' 카테고리의 다른 글
| Zustand로 전역 상태 관리하기 (0) | 2025.11.18 |
|---|---|
| aria-label과 텍스트 콘텐츠 (0) | 2025.11.13 |
| 웹 애플리케이션의 렌더링 방식과 Next.js (0) | 2025.11.08 |
| 타입스크립트 유틸리티 타입 (0) | 2025.11.05 |
| 타입스크립트의 동작 원리 (0) | 2025.10.27 |
