이 글은 아래 강의를 바탕으로 정리한 글입니다. 🤗
https://inf.run/UGoRu💡 TypeScript의 타입은 값의 집합입니다!
TS에서타입은 사실 여러 개의 값을 포함하는 집합입니다. 예를 들어-20, Infinity, 1, 2, 3, 123, 0.13과 같은 숫자 값들을 묶어 놓은 집합을 TS에서는number 타입이라고 부릅니다.
그렇다면 오직 하나의 값만 포함하는 타입인 Number Literal 타입은 딱 하나의 값만 포함하는 아주 작은 집합이라고 볼 수 있을 것 입니다.
그리고 이 number literal 타입의 값은 숫자이기 때문에 Number라는 거대한 집합에도 속하며, 결국 모든 number literal 타입은 number 타입이라는 거대한 집합에 포함되는 부분 집합으로 볼 수 있습니다.
이와 같이 TS의 모든 타입들은 집합으로써 서로를 포함하고 또, 포함되는 관계를 갖습니다.
이러한 관계 속에서 Number 타입처럼 다른 타입을 포함하는 타입을 **슈퍼 타입(부모 타입)**이라고 부릅니다. 반대로 슈퍼 타입에 속하는 타입은 **서브 타입(자식 타입)**이라고 합니다.
이 관계를 계층처럼 표시하면 다음과 같은 그림이 됩니다.


타입 호환성이란 A와 B 타입이 존재할 때, A 타입의 값을 B 타입으로 취급해도 괜찮은지 판단하는 것을 의미합니다. 그래서 만약 A 타입의 값이 B 타입의 값으로 취급되어도 괜찮다면 호환된다고 표현 합니다.
예를 들어 Number 타입과 Number Literal 타입이 있을 때, 서브 타입인 number literal 타입의 값을 슈퍼 타입인 number 타입의 값으로 취급하는 것은 가능하나 반대는 불가능 합니다. 즉, TS에서는 이렇게 슈퍼타입의 값을 서브타입의 값으로 취급하는 것을 허용하지 않습니다.
📌 서브 타입의 값을 슈퍼 타입의 값으로 취급하는 것은 업캐스팅,
슈퍼 타입의 값을 서브 타입의 값으로 취급하는 것을 다운캐스팅이라고 합니다.
정리하면 업캐스팅은 모든 상황에 가능하지만 다운캐스팅은 대부분의 상황에 불가능하다고 할 수 있습니다.

🌐 unknown 타입은 타입 계층도의 최상위!
unknown 타입이 타입 계층도에서 가장 위에 위치한다는 뜻은 unknown 타입은 모든 타입의 슈퍼타입이라는 의미이며, 모든 타입은 unknown 타입의 부분집합입니다.
따라서 unknown 타입 변수에는 모든 타입의 값을 할당할 수 있습니다. (업캐스트)
let a: unknown = 1; // number -> unknown
let b: unknown = "hello"; // string -> unknown
let c: unknown = true; // boolean -> unknown
let d: unknown = null; // null -> unknown
let e: unknown = undefined; // undefined -> unknown
let f: unknown = []; // Array -> unknown
let g: unknown = {}; // Object -> unknown
let h: unknown = () => {}; // Function -> unknown또한, 예외적인 경우가 아니면 다운캐스트는 허용되지 않는다고 하였기 때문에 unknown 타입의 값은 any를 제외한 어떤 타입의 변수에도 할당할 수 없습니다.
never 타입은 타입 계층도에서 가장 아래에 위치합니다.
즉, 모든 타입의 서브 타입이며, 공집합을 의미하는 타입입니다. 따라서 never 타입은 모든 타입으로 업캐스팅 할 수 있습니다.
let aa: number = neverFunc(); // never -> number
let bb: string = neverFunc(); // never -> string
let cc: boolean = neverFunc(); // never -> boolean
let dd: null = neverFunc(); // never -> null
let ee: undefined = neverFunc(); // never -> undefined
let ff: [] = neverFunc(); // never -> Array
let gg: {} = neverFunc(); // never -> Object
// let a: never = 1; // number -> never ❌
// let b: never = "hello"; // string -> never ❌
// let c: never = true; // boolean -> never ❌
// let d: never = null; // null -> never ❌
// let e: never = undefined; // undefined -> never ❌
// let f: never = []; // Array -> never ❌
// let g: never = {}; // Object -> never ❌void 타입은 계층도 상으로 보면 undefined의 슈퍼 타입임을 알 수 있습니다. 즉, undefined 타입의 값을 void 타입의 변수에 할당할 수 있다는 것을 의미합니다. (업캐스트)
let voidVar: void = undefined;위에서 말한 기본 타입 간의 호환성은 간단하게 정리하면 "특정 타입을 다른 타입으로 취급해도 괜찮은지 판단하는 것" 이었다면, 객체 타입 간의 호환성은 "어떤 객체 타입을 다른 객체 타입으로 취급해도 괜찮은지 판단하는 것" 입니다.
객체 타입 간의 호환성도 동일한 기준으로 판단합니다. 모든 객체 타입은 각각 다른 객체 타입들과 슈퍼-서브 타입 관계를 갖습니다. 따라서 업캐스팅은 허용하고 다운캐스팅은 허용하지 않습니다.
아래 예시를 보면 Animal 타입의 변수 animal에 Dog 타입의 변수 dog를 할당하는 것은 가능하나 반대로 Dog 타입의 변수 dog에 Animal 타입의 변수 animal을 할당하는 것은 불가능 합니다.
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 타입으로 취급 (다운캐스팅) 불가능왜냐하면 Animal 타입이 Dog 타입의 슈퍼타입이기 때문입니다.
💭 여기서 주의!
Dog 타입이 더 많은 프로퍼티를 정의하고 있어서 Animal 타입의 슈퍼타입이라고 생각할 수 있지만, 그렇지 않습니다.
TS는 프로퍼티를 기준으로 타입을 정의하는 구조적 타입시스템을 따르기 때문에:
따라서 어떤 객체가 Dog 타입에 포함된다면 무조건 Animal 타입에도 포함됩니다. 반대로 Animal 타입에 포함되는 모든 객체가 Dog 타입에 포함되는 것은 아닙니다. 그러므로 Animal은 Dog 타입의 슈퍼 타입이 되는 겁니다.
다른 예시로:
type Book = {
name: string;
price: number;
};
type ProgrammingBook = {
name: string;
price: number;
skill: string;
};
let programmingBook: ProgrammingBook = {
name: "한 입 크기로 잘라먹는 리액트",
price: 33000,
skill: "reactjs",
};
book = programmingBook; // ✅ OK
programmingBook = book; // ❌ NO
// ❌ 오류 발생
// let book2: Book = {
// name: "한 입 크기로 잘라먹는 리액트",
// price: 33000,
// skill: "reactjs",
// };⚡ 초과 프로퍼티 검사란?
위 코드에서 오류가 발생하는 이유는 Book 타입이 ProgrammingBook 타입의 슈퍼타입이긴 하지만 "초과 프로퍼티 검사"가 발동했기 때문입니다.
초과 프로퍼티 검사란 변수를 객체 리터럴로 초기화 할 때 발동하는 타입스크립트의 특수한 기능입니다. 이 기능은 타입에 정의된 프로퍼티 외의 다른 초과된 프로퍼티를 갖는 객체를 변수에 할당할 수 없도록 막습니다.
따라서 위 코드에서는 Book 타입에 정의되지 않은 skill 프로퍼티를 갖는 객체를 할당하려 했으므로 초과 프로퍼티 검사가 실패해 오류가 발생하는 것 입니다.
이러한 초과 프로퍼티 검사는 값을 별도의 다른 변수에 보관한 다음, 변수 값을 초기화 값으로 사용하면 발생하지 않습니다.
let book3: Book = programmingBook; // 앞서 만들어둔 변수