섹션 3. 타입스크립트 이해하기
4강. 객체 타입의 호환성
기본 타입 간의 호환성
특정 타입을 다른 타입으로 취급해도 괜찮은지 판단하는 것
let num1: number = 10;
let num2: 10 = 10;
num1 = num2;
객체 타입 간의 호환성
어떤 객체 타입을 다른 객체 타입으로 취급해도 괜찮은가?
타입스크립트 : 프로퍼티를 기준으로 타입을 정의하는 구조적 타입 시스템을 따른다.
Animal이 되려면 name, color만 있으면 되기 때문에 더 넓은 영역을 포괄한다. 슈퍼타입이 Animal, 서브타입이 Dog다.
즉, Dog는 name과 color가 있기 때문에 Animal의 범주 안에 들어가는 것이다. 따라서 Dog는 Animal이 될 수 있고, 이는 업캐스팅이다.
반대로, Animal은 dog가 되기에는 breed가 없기 때문에 다운캐스팅으로 불가능한 것이다.
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 : "white",
breed : "진도",
};
animal = dog; // 업캐스팅: 오류없음
// dog = animal; 오류발생: 다운캐스팅
5강. 대수 타입
대수 타입
여러 개의 타입을 합성해서 새롭게 만들어낸 타입
합집합 타입, 교집합 타입
1) 합집합 Union 타입
Dog와 Person은 서로가 슈퍼타입이 아닌 교집합을 공유하는 집합임.
Union1은 Dog에서 교집합을 뺀 부분에, Union2는 Person에서 교집합을 뺀 부분에, Union3은 Dog와 Person의 교집합에 위치해 있음. 하지만 Union4는 어느 곳에도 속하지 못하고 합집합 외부에 있어서 에러가 나는 것임.
/**
* 1. 합집합 - Union 타입
*/
let a : string | number | boolean;
a = 1;
a = "Hello";
a = true;
let arr: (number | string | boolean)[] = [1, "Hello", true];
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 타입
기본 타입들 중에는 공유하거나 겹치는 값이 없어서 결괏값이 never, 공집합이 나오는 경우가 많다.
따라서 대부분 객체타입에 사용한다.
Dog와 Person에 동시에 속하려면 name, color, language를 모두 갖고 있어야 한다.
그래서 위에서 language가 없으면 에러가 나는 것이다.
6강. 타입 추론
1) 타입 추론
다음과 같은 코드를 썼을 때, 타입을 알아서 추론한다.
let a = 10;
let b = "hello";
let c = {
id : 1,
name : "Chobo",
profile : {
nickname : "Chobo-coder",
},
urls : ["https://coder-chobo.tistory.com"],
};
let { id, name, profile } = c;
let [one, two, three] = [1, "hello", true];
a는 number, b는 string, c는 함수 안의 string으로 추론한다.
let [one, two, three] = [1, "hello", true];
텍스트로 써있는 배열도 추론한다.
- one : number
- two : string
- three : boolean
따로 지정하지 않아도 알아서 추론한다.
2) 암묵적 any
위에서 변수 d를 선언하고 따로 지정해주지 않으면 암묵적으로 any 타입으로 지정한다.
그래서 위에서 d=10을 지정한 이후에는 d는 number 형식으로 들어가고,
그 밑에 줄에서 d="hello"로 지정되면 d를 string으로 인식한다.
그래서 위 단락과 아래 단락에서 각각 쓸 수 있는 함수가 다른 것이다.
이런 모든 케이스를 외울 필요는 없고, 명확하게 지정하지 않으면 암묵적으로 any로 지정해서 변화무쌍해진다는 것을 알아두면 된다.
3) 최적 공통 타입(Best Common Type)
배열이면 최대한 공통적인 타입으로 추론한다.
4) 타입 넓히기
좀 더 범용적으로 사용할 수 있도록 조금 더 넓은 타입으로 추론하는 과정을 타입 넓히기라고 한다.
let a = 10; //number 타입
const num = 10; //리터럴 타입
7강. 타입 단언(Assertion)
1) 타입 단언
값 as 타입 으로 특정 값을 원하는 타입으로 단언할 수 있다.
type Person = {
name : string;
age : number;
};
let person = {} as Person;
person.name = "초보코더";
person.age = 20;
peson은 Person 타입으로 정의되었지만 초기화하면서 빈 객체를 넣어두고 싶을 때, 빈 객체는 Person 타입이 아니기 때문에 오류가 발생하게 된다. 이때, 빈 객체를 Person 타입이라고 TS에 단언해 주면 된다.
type Dog = {
name : string;
color : string;
};
let dog = {
name : "백구",
color : "white",
breed : "진도",
} as Dog;
breed라는 초과 프로퍼티를 Dog 타입으로 단언하여 초과 프로퍼티 검사를 피할 수 있다.
2) 타입 단언의 규칙
- 단언식 : 값 as 단언
- A as B
- A가 B의 슈퍼타입이거나
A가 B의 서브타입이어야 한다.
단언하기 위해서는 겹치는 부분이 충분히 있어야 한다.
그래서 number과 string은 겹치는 부분이 없기 때문에 에러가 나는 것이다.
3) const 단언
뒤에 const 단언을 해주면 readonly가 자동으로 들어간다.
이를 이용하면 편하게 readonly를 작동시킬 수 있다.
const 단언은 변수를 const로 선언한 것과 비슷하게 타입이 변경된다.
4) Non Null 단언
type Post = {
title : string;
author? : string;
};
let post : Post = {
title : "게시글1",
author : "초보코더",
};
이렇게 코딩하면 author는 있을수도(string), 없을 수도(undefined) 있다.
const len1 : number = post.author?.length;
//에러 : 'number | undefined' 형식은 'number' 형식에 할당할 수 없습니다.
//'undefined' 형식은 'number' 형식에 할당할 수 없습니다.
따라서 이렇게 코딩하면 에러가 난다. 이걸 해결하기 위해 ? 대신 !를 붙여주면 되는데, !가 Non Null 연산자다.
const len2 : number = post.author!.length;
이러면 author는 반드시 있다고 표현해주는 것이기 때문에, author가 string이 되고, post.author.length는 숫자로 표현이 되기 때문에 에러가 없는 것이다.
타입 단언은 실제로 타입을 바꾸는 건 아니다.
컴파일러의 눈을 잠깐 가리는 기능이다.
따라서, 잘못된 코드를 오류가 없다고 처리할 수 있기 때문에 위험하다.
꼭 필요한 경우에만 쓰는게 좋다.