1. 타입은 집합이다.
- 타입스크립트의 타입은 여러 개의 값을 포함하는 집합이다.
- 집합은 동일한 속성을 갖는 여러개의 요소들을 하나의 그룹으로 묶은 단위를 말한다.
- 타입스크립트의 모든 타입들은 집합으로써 서로 포함하고 또 포함되는 관계를 갖는다.
- 다른타입을 포함하는 타입을 슈퍼 타입이라고 하고 다른 타입에 포함되는 타입을 서브 타입이라고 한다.
- 이러한 집합 관계는 계층 구조로 표현할 수 있는데 이걸 통해 다양한 타입의 관계를 이용할 수 있다.
2. 타입 호환성
- 예를 들어 number 타입과 number 리터럴 타입이 있을 때 서브 타입인 number 리터럴 타입의 값을 슈퍼 타입인 number 타입의 값으로 취급하는 건 가능하지만 반대는 불가능하다. 왜냐하면 number 타입이 더 큰 타입이기 때문이다.
let num1 : number = 10;
let num2: 10 = 10;
num1 = num2; // 가능!
num2 = num1; // 오류! 불가능
- 이처럼 number 타입의 변수 num1에 number 리터럴 타입 변수인 num2를 할당하는 건 가능하다. num1이 더 큰 타입이기 때문이다. 하지만 그 반대는 불가능하다.
- number 타입은 숫자 10 이외에도 더 다양한 숫자 값을 담을 수 있는 반면 리터럴 타입은 할당해준 10 이외에는 담을 수 없기 때문이다.
- 서브 타입의 값을 슈퍼 타입의 값으로 취급하는 것을 업 캐스팅이라고 한다.
- 슈퍼 타입의 값을 서브 타입의 값으로 취급하는 것을 다운 캐스팅이라고 한다.
- 대부분 업 캐스팅은 가능하고 다운 캐스팅은 불가능하다.
2.1 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
let unknownValue: unknown;
let a: number = unknownValue;
// 오류 : unknown 타입은 number 타입에 할당할 수 없습니다.
- unknown 타입에는 모든 타입을 할당할 수 있지만 그 반대는 불가능하다. 즉, 다운 캐스팅이 불가능하다.
- unknown 타입의 값은 any를 제외한 어떤 타입의 변수에도 할당할 수 없다.
2.2 never 타입(공집합)
- 가장 아래에 위치한 never 타입은 모든 타입의 부분 집합이며 서브 타입이다.
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 ❌
- never 타입은 모든 타입의 서브 타입으로 업 캐스팅 할 수 있다.
- 반대로 어떤 타입도 never 타입으로 다운 캐스팅 할 수 없다.
2.3 void 타입
- void 타입은 아무것도 반환하지 않는 함수의 반환값 타입으로 주로 사용된다.
function noReturnFuncA(): void {
return undefined;
}
function noReturnFuncB(): void {
return;
}
function noReturnFuncC(): void {}
- void 타입은 undefined 타입의 슈퍼 타입이다. 따라서 void 타입을 선언한 함수에서 undefined 타입를 반환해도 오류가 나지않는다. undefined 타입은 서브 타입으로 업 캐스팅이 가능하기 때문이다.
- void 타입의 서브 타입은 undefined 타입과 never 타입 밖에 없기 때문에 둘 이외의 값을 할당할 수 없다.
💡치트키라고 불리는 any 타입은?
→ any 타입은 치트키 답게 모든 타입의 슈퍼 타입이 될수도 서브 타입이 될 수도 있다. 즉, 모든 타입으로 업캐스팅 할수도 다운캐스팅 할 수도 있다.
3. 객체 타입의 호환성
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에 dog를 할당하는 건 가능하지만 서브 타입인 dog에 animal을 할당하는 건 불가능하다.
❓ 근데 여기서 프로퍼티가 더 많은 Dog 타입이 왜 슈퍼 타입이 아닌걸까?
- 타입스크립트는 프로퍼티를 기준으로 타입을 정의하는 구조적 타입 시스템을 따른다.
- Animal 타입은 name과 color 모든 프로퍼티를 갖는 즉, 모든 객체들을 포함하는 집합이다.
- Dog 타입은 name과 color를 가지고 있고 거기에 추가로 breed 프로퍼티를 가지고 있는 집합이다.
- 어떤 객체가 Dog 타입에 포함된다면 무조건 Animal 타입에도 포함된다. 하지만 반대는 불가능하다. 따라서 Animal 타입이 Dog 타입의 슈퍼 타입인 것이다.
3.1 초과 프로퍼티 검사
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",
};
let book2: Book = { // 오류 발생
name: "한 입 크기로 잘라먹는 리액트",
price: 33000,
skill: "reactjs",
};
let book3: Book = programmingBook; // 앞서 만들어둔 변수 사용
- 이때 업 캐스팅에 해당하는데도 오류가 나는 이유는 초과 프로퍼티 검사를 하기 때문이다.
- 초과 프로퍼티 검사란 변수를 객체 리터럴로 초기화 할 때 일어나는 타입스크립트의 기능이다.
- 이 기능은 타입에 정의된 프로퍼티 이외의 다른 초과된 프로퍼티를 갖는 객체를 변수에 할당할 수 없도록 만든다.
- 이 기능은 단순히 변수를 초기화할 때 객체 리터럴을 사용하지만 않으면 발생하지 않는다.
- 값을 별도의 다른 변수에 보관한 다음 변수 값을 초기화 값으로 사용하면 된다.
function func(book: Book) {}
func({ // 오류 발생
name: "한 입 크기로 잘라먹는 리액트",
price: 33000,
skill: "reactjs",
});
func(programmingBook); // 변수 사용
- 초과 프로퍼티 검사는 함수의 매개변수에도 발생한다.
- 함수의 매개변수에 인수로 값을 전달하는 과정도 변수를 초기화 하는 과정과 같다.
- 이때 검사를 피하고 싶다면 미리 값을 변수에 담아둔 다음 변수값을 인수로 전달하면 된다.
4. 대수타입(Algebraic type)
- 여러 개의 타입을 합성해서 만드는 타입
- 합집합 Union 타입, 교집합 Intersection 타입
4.1 Union 타입
// 합집합 타입 - Union 타입
let a: string | number | boolean;
a = 1;
a = "hello";
a = true;
- union 타입은 합집합 타입으로 number 타입 이거나 string 타입 이거나 boolean 타입을 뜻한다.
- | 를 사용한다.
- 위의 예제와 같이 union 타입으로 정의된 변수에는 number, string, boolean 타입에 해당한 값이라면 뭐든 저장할 수 있다.
4.2 Union 타입으로 배열 타입 정의하기
let arr: (number | string | boolean)[] = [1, "hello", true];
- 유니온 타입을 이용하면 다양한 타입의 요소를 보관하는 배열 타입을 손쉽게 정의할 수 있다.
4.3 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: "",
};
- 여러개의 객체 타입의 유니온 타입도 정의할 수 있다.
- 이렇게 정의된 Union1 타입은 교집합이 존재하는 두 집합으로 표현할 수 있다.
- 따라서 위의 예제같이 포함하는 타입과 포함하지 않는 타입으로 사용할 수 있다.
4.4 Intersection 타입
let variable: number & string;
// never 타입으로 추론된다
- Intersection 타입은 교집합 타입으로 number 타입과 string 타입은 서로 교집합을 공유하지 않는 서로소 집합이므로 변수 타입은 never 타입으로 추론된다.
- & 를 사용한다.
- 대다수의 기본 타입들 간에는 서로 공유하는 교집합이 없기 때문에 인터섹션 타입은 보통 객체 타입들에 자주 사용된다.
4.5 Intersection 타입과 객체 타입
type Dog = {
name: string;
color: string;
};
type Person = {
name: string;
language: string;
};
type Intersection = Dog & Person;
let intersection1: Intersection = {
name: "",
color: "",
language: "",
};
- 두 객체 타입의 인터섹션 타입을 정의 할 수 있다.
📌
'Front-end > TypeScript' 카테고리의 다른 글
[TS] 타입을 추론하고... 단언하고... 좁히고... (0) | 2024.02.19 |
---|---|
[TS] 타입 별칭(type) vs 인터페이스(interface) (1) | 2024.02.18 |
[TS] 타입스크립트의 기본 타입 (0) | 2024.02.16 |
[TS] 타입스크립트 컴파일러 옵션 알아보기 (0) | 2024.02.15 |
[TS] 꼭 타입스크립트를 사용해야 할까? (0) | 2024.02.14 |