타입은 집합이다
- 타입스크립트의 모든 타입들은 집합으로써 서로 포함하고 포함되는 관계를 갖는다.
- 그리고 이런 관계에서 Number 타입처럼 다른 타입을 포함하는 타입을 **슈퍼 타입(부모 타입)**이라고 부릅니다. 반대는 **서브 타입(자식 타입)**이라고 한다.
1. 타입 호환성
A와 B 두개의 타입이 존재할 때 A 타입의 값을 B 타입으로 취급해도 괜찮은지 판단하는 것을 의미한다. 만약 A 타입의 값이 B 타입의 값으로 취급 되어도 괜찮다면 호환된다고 하고 안된다면 호환되지 않는다고 함.
- 따라서 타입스크립트에서는 이렇게 슈퍼타입의 값을 서브타입의 값으로 취급하는것을 허용하지 않는다.
- ex)
// 가능
let num1: number = 10;
let num2: 10 = 10;
num1 = num2;
// 불가능
let num1: number = 10;
let num2: 10 = 10;
num2 = num1; // 오류
- 업캐스팅 : 서브 타입의 값을 슈퍼 타입의 값으로 취급하는 것
- 다운캐스팅 : 슈퍼 타입의 값을 서브타입의 값으로 취급 ( 대부분 불가능 )
2. unknown 타입 (전체 집합)
unknown 타입은 타입 계층도의 최 상단에 위치
- unknown 타입 변수에는 모든 타입의 값을 할당할 수 있다. (모든 타입은 unknown 타입으로 업 캐스트 가능)
- unknown 타입의 값은 any를 제외한 어떤 타입의 변수에도 할당할 수 없다.
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
let unknownValue: unknown;
let a: number = unknownValue;
// 오류 : unknown 타입은 number 타입에 할당할 수 없습니다.
3. never 타입 (공집합 타입)
- never 타입은 타입 계층도에서 가장 아래에 위치합니다.
- never 타입에 해당하는 값은 말 그대로 아무것도 없다.
ex.
- errorFunc 함수는 에러를 발생시킴 → 이 함수는 정상적으로 종료되지 않는다.
- 그러므로 어떤 값도 반환할 수 없다
function errorFunc(): never {
throw new Error();
}
- 쉽게 업캐스팅 가능
let neverVar: never;
let a: number = neverVar; // never -> number
let b: string = neverVar; // never -> string
let c: boolean = neverVar; // never -> boolean
let d: null = neverVar; // never -> null
let e: undefined = neverVar; // never -> undefined
let f: [] = neverVar; // never -> Array
let g: {} = neverVar; // 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 ❌
4. void 타입
아무것도 반환하지 않는 함수의 반환값 타입으로 주로 사용
ex.
function noReturnFunc(): void {
console.log("hi");
}
- void 타입은 undefined 타입과 never타입의 슈퍼타입이므로 반환값을 void로 선언한 함수에서 undefined을 반환 해도 오류가 발생하지 않음
function noReturnFuncA(): void {
return undefined;
}
function noReturnFuncB(): void {
return;
}
function noReturnFuncC(): void {}
- void 타입의 서브타입은 undefined 타입과 never 타입 밖에 없다.
- 따라서 void 타입에는 undefined, never 이외에 다른 타입의 값을 할당할 수 없다.
let voidVar: void;
voidVar = undefined; // undefined -> void (ok)
let neverVar: never;
voidVar = neverVar; // never -> void (ok)
5. any 타입
모든 타입으로 다운캐스트 할 수 있으며 또 모든 타입은 any 타입으로 업 캐스트 할 수 있다.
- 모든 타입의 슈퍼타입이 될 수도 있고 모든 타입의 서브 타입이 될 수도 있음
- 각각 number, string, boolean 타입을 갖는 변수 num, str, bool에 any 타입의 값을 할당
- 이는 any 타입이 각각 number, string, boolean 타입으로 다운 캐스트 된다고 이해 가능함
- any 타입 변수 anyValue에 num, str, bool 변수에 담긴 값을 할당합니다.
- 이는 number, string, boolean 타입이 모두 any 타입으로 업 캐스트 되는 것으로 이해 가능함
let anyValue: any;
let num: number = anyValue; // any -> number (다운 캐스트)
let str: string = anyValue; // any -> string (다운 캐스트)
let bool: boolean = anyValue; // any -> boolean (다운 캐스트)
anyValue = num; // number -> any (업 캐스트)
anyValue = str; // string -> any (업 캐스트)
anyValue = bool; // boolean -> any (업 캐스트)
객체 타입의 호환성
1. 객체 타입의 호환성
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; // ✅ OK
dog = animal; // ❌ NO
- Animal 타입의 변수 animal에 Dog 타입의 변수 dog를 할당하는 것은 가능
- 그러나 반대로 dog 변수에 animal 변수의 값을 할당하는 것은 불가능
- Animal 타입이 Dog 타입의 슈퍼타입이기 때문
💡
Animal 타입이 Dog 타입의 슈퍼타입인 이유
- 타입스크립트는 프로퍼티를 기준으로 타입을 정의하는 구조적 타입 시스템을 따른다.
- Animal 타입은 name과 color 프로퍼티를 갖는 모든 객체들을 포함하는 집합이고, Dog 타입은 name과 color 거기에다 추가로 breed 프로퍼티를 갖는 모든 객체를 포함하는 집합이다.
- 그러므로 어떤 객체가 Dog 타입에 포함된다면 무조건 Animal 타입에도 포함된다.
- 그러나 반대로 Animal 타입에 포함되는 모든 객체가 Dog 타입에 포함되는것은 아니다.
ex.
type Book = {
name: string;
price: number;
};
type ProgrammingBook = {
name: string;
price: number;
skill: string;
};
let book: Book;
let programmingBook: ProgrammingBook = {
name: "한 입 크기로 잘라먹는 리액트",
price: 33000,
skill: "reactjs",
};
book = programmingBook; // ✅ OK
programmingBook = book; // ❌ NO
2. 초과 프로퍼티 검사
변수를 객체 리터럴로 초기화 할 때 발동하는 타입스크립트의 특수한 기능 이 기능은 타입에 정의된 프로퍼티 외의 다른 초과된 프로퍼티를 갖는 객체를 변수에 할당할 수 없도록 막는다.
type Book = {
name: string;
price: number;
};
type ProgrammingBook = {
name: string;
price: number;
skill: string;
};
let book2: Book = { // 오류 발생
name: "한 입 크기로 잘라먹는 리액트",
price: 33000,
skill: "reactjs",
};
- 위 코드는 Book 타입에 정의되지 않은 skill 프로퍼티를 갖는 객체를 할당하려고 했으므로 초과 프로퍼티 검사가 실패해 오류가 발생
- 이런 초과 프로퍼티 검사는 단순히 변수를 초기화 할 때 객체 리터럴을 사용하지만 않으면 발생하지 않는다.
- 따라서 다음과 같이 값을 별도의 다른 변수에 보관한 다음 변수 값을 초기화 값으로 사용하면 발생하지 않음
ex.
let book3: Book = programmingBook; // 앞서 만들어둔 변수
- 초과 프로퍼티 검사는 함수의 매개변수에도 동일하게 발생
function func(book: Book) {}
func({ // 오류 발생
name: "한 입 크기로 잘라먹는 리액트",
price: 33000,
skill: "reactjs",
});
- 함수의 매개변수에 인수로 값을 전달하는 과정도 변수를 초기화 하는 과정과 동일함
- 따라서 초과 프로퍼티 검사가 발동
- 이때에도 역시 검사를 피하고 싶다면 다음과 같이 변수에 미리 값을 담아둔 다음 변수값을 인수로 전달하면 된다
ex.
func(programmingBook);
대수 타입
대수 타입이란 여러개의 타입을 합성해서 만드는 타입 (합집합은 Union 타입, 교집합은 Intersection 타입)
1. 합집합(Union) 타입
바 | 를 이용
// 합집합 타입 - Union 타입
let a: string | number;
a = 1;
a = "hello";
- 변수 a에는 number 타입과 string 타입에 해당하는 값이라면 뭐든 저장할 수 있다.
1) Union 타입으로 배열 타입 정의하기
1let arr: (number | string | boolean)[] = [1, "hello", true];
2) 객체타입의 Union타입
type Dog = {
name: string;
color: string;
};
type Person = {
name: string;
language: string;
};
type Union1 = Dog | Person;
let union1: Union1 = { // ✅
name: "",
color: "",
};
let union2: Union1 = { // ✅
name: "",
language: "",
};
let union3: Union1 = { // ✅
name: "",
color: "",
language: "",
};
let union4: Union1 = { // ❌
name: "",
};
2. 교집합(Intersection) 타입
&을 이용
let variable: number & string;
// never 타입으로 추론된다
- 서로 교집합을 공유하지 않는 서로소 집합이므로 변수 variable의 타입은 결국 never 타입으로 추론
- 대다수의 기본 타입들 간에는 서로 공유하는 교집합이 없기 때문에 이런 인터섹션 타입은 보통 객체 타입들에 자주 사용됩니다.
1) 객체 타입의 Intersection 타입
type Dog = {
name: string;
color: string;
};
type Person = {
name: string;
language: string;
};
type Intersection = Dog & Person;
let intersection1: Intersection = {
name: "",
color: "",
language: "",
};
'Frontend > TypeScript' 카테고리의 다른 글
[TypeScript] 4. 인터페이스 (0) | 2025.01.26 |
---|---|
[TypeScript] 3. 함수와 타입 (0) | 2025.01.25 |
[TypeScript] 2-2. 타입스크립트 이해하기 - 추론과 단언 (0) | 2025.01.24 |
[TypeScript] 1. 타입스크립트 기본 (1) | 2025.01.22 |
[TypeScript] 0. 타입스크립트 개요 (0) | 2025.01.21 |