Today I Learned_230407

2 분 소요

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();

카테고리:

업데이트: