Today I Learned_230407
TypeScript
섹션 11. 두 번째 프로젝트 - 전화번호부 애플리케이션
타입을 강력하게 강제하려면 tsconfig에 아래 세 가지 옵션 추가
"noImplicitAny": true,
"strict": true,
"strictFunctionTypes": true
어떤 키가 들어오던 간에 그 값은 num : 숫자가 와야 한다.
interface PhoneNumberDictionary {
[phone: string]: {
num: number;
};
}
제네릭 - API 응답의 규격을 받을 때 제네릭을 굉장히 많이 사용한다.
타입이 명확할 경우, 명시해놓지 않더라도 타입스크립트가 추론을 잘 해 주는 경우가 있음
function fetchItems() {
const items = ['a', 'b', 'c'];
return items;
}
// 이 경우, string[]이 분명하므로 string[]으로 추론.
// 굳이 return type을 string[]이라고 하지 않아도 된다.
실행이 바로 가능해서 확인할 수 있는 동기적인 구조에서는 추론이 가능.
하지만 비동기적인 코드라면?
function fetchItems2() {
const items = ['a', 'b', 'c'];
return new Promise(function (resolve) {
resolve(items);
});
}
이 경우 함수의 return값은 string[]이 아닌 Promise
Promise의 생성자를 반환하면 Promise
함수가 실행되는 시점에서 타입스크립트가 promise 안에 들어오는 비동기 코드에 대해 알 수 없기 때문.
lib.es2015.promise.d.ts에 기술된 promise 생성자는 아래와 같다.
new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
즉, new Promise를 했을 때의 return type은 Promise
따라서 반환값이 무엇인지 명시를 해 주어야지 promise를 제대로 쓸 수 있다.
function fetchItems2(): Promise<string[]> {
const items = ['a', 'b', 'c'];
return new Promise(function (resolve) {
resolve(items);
});
}
promise 안에서 resolved된 값이 반환 타입으로 들어가 있어야 한다.
items는 string[]이 되어야 한다.
실전 : Axios 반환 - 이 때 타입을 지정해 주자.
fetchContacts의 리턴 타입 → Promise
function fetchContacts(): Promise<Contact[]> {
const contacts: Contact[] = [
{
....
},
];
// Promise를 이용한 mocking : 2초 뒤에 아래의 값이 온다.
// Promise를 return하고 있으므로 return 타입은 Promis로 주자.
return new Promise(resolve => {
setTimeout(() => resolve(contacts), 2000);
});
}
Promise의 resolve는 Contact[]타입이다.
map 예시
// map 예시
let heroes = [
{ name: 'Tony', age: 30 },
{ name: 'Captain', age: 100 },
];
//result : ['Tony', 'Captain']
heroes.map(function (hero) {
return hero.name;
});
class main 구현
class AddressBook {
contacts: Contact[] = [];
//constructor는 기본적인 타입 정의가 되지 않는다.
constructor() {
this.fetchData();
}
fetchData(): void {
// Promise가 Promise<Contact>이므로 response는 Contact[] 타입
fetchContacts().then(response => {
this.contacts = response; //따라서 Contact[]타입의 this.contacts에 response가 들어갈 수 있다.
});
}
// Contact의 name과 동일한 타입을 가져가야 하므로 string
findContactByName(name: string): Contact[] {
// 필터를 하면 배열을 반환하므로 Contact[]
// 필터를 하면 요소에 대한 타입을 볼 수 있음. contact.name은 string이니 name은 string이 되어야 한다.
return this.contacts.filter(contact => contact.name === name);
}
findContactByAddress(address: string): Contact[] {
return this.contacts.filter(contact => contact.address === address);
}
//phonetype은 home, office, studio가 될 것.
//phonetype에 이상한 데이터 넣으면 찾을 수 없다.. string이었는데 이걸 enum으로 만들어 보자.
findContactByPhone(phoneNumber: number, phoneType: PhoneType): Contact[] {
return this.contacts.filter(
contact => contact.phones[phoneType].num === phoneNumber
);
}
addContact(contact: Contact): void {
this.contacts.push(contact);
}
displayListByName(): string[] {
// 이름을 가지고 map을 돌린다. name만 가지고 뽑아 배열을 만든다.
// 기존 배열을 변환하여 새로운 배열을 만드는 것.
return this.contacts.map(contact => contact.name);
}
displayListByAddress(): string[] {
return this.contacts.map(contact => contact.address);
}
}
new AddressBook();