이 글은 인프런의 실전 프로젝트로 배우는 TypeScript 강의를 듣고 정리한 글입니다.
function $(selector) {
return document.querySelector(selector);
}
function getUnixTimestamp(date) {
return new Date(date).getTime();
}
이 두 자바스크립트 함수를 보자. 기능 자체는 생각하지 말고 코드만 보면, 첫번째 함수의 selector의 타입은 어떻게 될까?
아마 높은 확률로 string일 것이다. 왜냐하면, querySelector 뒤에는 클래스면 ".클래스이름", id면 "#id이름" 이 들어오게 되는데, 어쨌든 간에 id나 클래스의 이름은 문자열로 되어있기 때문이다.
두 번째로 getUnixTimestamp를 보면, return 문에서 자바스크립트의 내장 함수 new Date를 불러온다. 이러한 자바스크립트 내장 함수를 가져올 경우, 타입이 알아서 추론이 된다.
따라서, 아래와 같이 타이핑을 할 수 있겠다.
function $(selector: string) {
return document.querySelector(selector);
}
function getUnixTimestamp(date: string | number | Date) {
return new Date(date).getTime();
}
이제 다음으로 함수 하나를 더 볼 것이다.
function createSpinnerElement(id) {
const wrapperDiv = document.createElement("div");
wrapperDiv.setAttribute("id", id);
(후략)
}
여기에서 id의 type은 어떻게 될까? 이 코드들은 한 프로젝트에 대한 코드들이지만, 여기서는 기능보다 타이핑에 집중하기 때문에 타이핑과 관련되지 않은 부분은 생략하였다. wrapperDiv 변수는, div 태그를 만드는 명령이고, 그 아랫줄을 통해 id 속성을 id라는 파라미터로 부여하려고 하는 것임을 알 수가 있다. 이에 따르면, 위에 $(selector) 함수처럼, id는 string으로 들어가야 하므로
function createSpinnerElement(id: string) {
const wrapperDiv = document.createElement("div");
wrapperDiv.setAttribute("id", id);
(후략)
}
이렇게 string으로 타이핑할 수 있겠다.
그렇다면, 쿼리가 들어가 있는 부분의 타입은 어떻게 해야될까?
enum CovidStatus {
Confirmed = 'confirmed',
Recovered = 'recovered',
Deaths = 'deaths',
}
function fetchCountryInfo(countryCode: string, status: CovidStatus) {
// 특정 국가의 코로나 정보 받아오기
// status params: confirmed, recovered, deaths
const url = `https://api.covid19api.com/country/${countryCode}/status/${status}`;
return axios.get(url);
}
특정 국가의 코로나 관련 정보를 받아오는 API 함수가 있다고 하자. 이 경우 url에서 보면, countryCode와 status를 파라미터로 받아오게 되어있다. 이 부분은 실제 사용례를 보면 알 수 있다. 링크에서 보면, countryCode에는 south-africa가, status에는 위 코드에서 주석으로 달았듯이 confirmed, recovered, deaths 중 하나가 들어갈 수 있다. 그리하여, 두 개 다 string이 들어가는 것이 맞는데, CovidStatus 같은 경우는 들어가는 요소가 한정적이므로 enum으로 나타내었다.
다음으로, Element typing에 대해서 타입 단언을 이용해서 알아볼 것이다.
HTML
<span class="confirmed-total"></span>
<p class="total deaths">0</p>
TS
function $(selector: string) {
return document.querySelector(selector);
}
const deathsTotal = $(".deaths");
const confirmedTotal = $(".confirmed-total");
function setTotalDeathsByCountry(data: any) {
deathsTotal.innerText = data[0].Cases;
}
function setTotalConfirmedNumber(data: any) {
confirmedTotal.innerText = data.Countries.reduce(
(total: any, current: any) => (total += current.TotalConfirmed),
0
);
}
이렇게 HTML 클래스와 그에 대한 타입스크립트 함수가 있다고 하자. 우선 각 함수에 대해 data를 any로 처리해뒀었다. 그러고서 확인해보니..
위와 같은 에러를 출력하고 있었다. 여기서 Element가 뭔지 살펴보고 가야한다. TypeScript 공식 문서에서 Element를 보면,
Element is the most general base class from which all objects in a Document inherit. It only has methods and properties common to all kinds of elements. More specific classes inherit from Element.
직역하면, "Element는 Document의 모든 개체가 상속하는 가장 일반적인 기본 클래스입니다. 모든 종류의 요소에 공통된 메서드와 속성 만 있습니다. 보다 구체적인 클래스는 Element에서 상속됩니다." 라고 할 수 있다. 모든 개체는 Element의 상속을 받는다.
이러한 Element의 형식에 innerText가 없다는 에러를 출력하는 것이다. 이는 즉, Element의 상속을 받는 어떤 것을 타입으로 선언해줘야 이러한 에러를 해결할 수 있다는 것이다. 그게 아니라면, HTML에 타이핑을 어떻게 할 수 있겠는가?
타입 단언을 통해 이러한 문제를 해결할 수 있다. HTML의 각 태그 별로 element 타입이 존재한다. 예를 들면 p 태그의 경우 HTMLParagraphElement, span 태그의 경우 HTMLSpanElement와 같은 식으로 말이다.
따라서,
const deathsTotal = $(".deaths") as HTMLParagraphElement;
const confirmedTotal = $(".confirmed-total") as HTMLSpanElement;
이와 같이 타입 단언을 해줘야 한다.
그런데, 여기서 의문이 들 수 있다. 왜 변수에 선언하지 않고 반환값에 타입을 선언하는가?
-> 그 이유는 $ 함수 때문이다.
function $(selector: string) {
return document.querySelector(selector);
}
이 함수를 보면, 파라미터에 대한 타이핑은 되어있으나 반환값에 대한 타이핑이 되어있지 않다. 그도 그럴 것이, 어떤 것이 반환될 지 당연히 함수 선언 시점에서는 알 수가 없기 때문이다. $를 사용하는 모든 함수들에 대해 타이핑을 한꺼번에 할 수는 없는 노릇이다.
결국, 반환값에 대한 타이핑이 없으므로 이를 타입 단언을 통해 선언해 준 것이다.
'Web' 카테고리의 다른 글
[API통신] withCredentials, SWR (0) | 2021.07.05 |
---|---|
[TypeScript] 회원가입 페이지 구현하기 (0) | 2021.07.05 |
[TypeScript] 자바스크립트에서 타입스크립트로 변환하기 (0) | 2021.06.30 |
[TypeScript] 맵드 타입(Mapped Type) (0) | 2021.06.28 |
[TypeScript] 유틸리티 타입 (0) | 2021.06.27 |