JavaScript를 TypeScript로 변환하기🦾
비동기 처리
유저 정보를 받아오는 비동기 처리 함수를 생각해보자.
기존 JS 코드는 다음과 같다.
const getUserProfile = async ({ id }) => {
let config = {
method: "get",
url: `${URL}/auth/profile/${id}`,
};
try {
const _res = await axios.get(`${URL}/auth/profile/${id}`);
return _res.data;
} catch (e) {
throw e;
}
};
항상 기억하자 → TS는 JS의 변수, 매개 변수, 리턴값에 타입을 지정한 것!
우선 가장 먼저 눈에 띄는(그리고 선명한 빨간 밑줄이 그여있는) 매개변수의 타입을 지정해주자.
const getUserProfile = async ({ id }: { id: number }) => {
let config = {
method: "get",
url: `${URL}/auth/profile/${id}`,
};
try {
const _res = await axios.get(`${URL}/auth/profile/${id}`);
return _res.data;
} catch (e) {
throw e;
}
};
에러가 사라졌다. 성공!!!
...인 줄 알았지만... 위 함수에 마우스를 올려보면...
해당 함수의 리턴값이 Promise<any>임을 확인할 수 있다.
async 키워드로 인해 getProfile 함수는 항상 Promise를 return할테니, Promise로 자동 타입 추론이 이뤄진 건 당연하지만...
any 타입 = 어떠한 타입도 할당이 가능하다 → 타입 추론을 포기하겠다 → TS를 사용하는 의미가 없다!!!
즉 getUserProfile 함수의 리턴값에 대한 타입을 좀 더 명시해줄 필요가 있겠다.
그런데 일단 Promise란 interface의 첫번째 제네릭은 어떤 의미지?
interface Promise<T> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
): Promise<TResult1 | TResult2>;
/**
* Attaches a callback for only the rejection of the Promise.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of the callback.
*/
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}
아하!
Promise라는 interface는 then, catch라는 메서드를 가지고 있으며,
이 때 제네릭 T는 onfulfilled callback의 매개변수 value의 타입 이자,
onfulfilled callback의 리턴값의 타입(TResult1 = T)을 의미했다!
→ 결론적으로... Promise의 제네릭은 해당 Promise가 fulfilled 됬을 때 반환받는 데이터 타입을 의미한다!
즉 getUserProfile함수의 리턴값인 Promise<any>는, fulfilled시 any 타입을 리턴하는 Promise를 뜻하는 것!
따라서... 외부에서 해당 비동기 요청의 결과 타입을 추론할 때는 'any 타입의 무언가가 반환됨' 그 이상은 알 수 없다. 이런!
그렇다면 해결 방안은? → 리턴값의 타입을 좁혀주면 될 일.
getUserProfile 함수의 리턴값은 id에 해당되는 유저의 정보일테니...
interface UserInfo {
id: number;
name: string;
}
const getUserProfile = async ({ id }: { id: number }): UserInfo => {
let config = {
method: "get",
url: `${URL}/auth/profile/${id}`,
};
try {
const _res = await axios.get(`${URL}/auth/profile/${id}`);
return _res.data;
} catch (e) {
throw e;
}
};
UserInfo란 Interface를 선언한 후, getUserProfile 함수의 리턴값으로 타이핑 해줬다.
그럼 곧바로 에러를 만나는데...
앞서 작성한 것 처럼 async 키워드로 인해 해당 함수는 항상 Promise 객체를 리턴한다.
그런데 우리는 리턴값을 UserInfo로 타이핑 해줬으니 에러가 날 수 밖에!
interface UserInfo {
id: number;
name: string;
}
const getUserProfile = async ({ id }: { id: number }): Promise<UserInfo> => {
let config = {
method: "get",
url: `${URL}/auth/profile/${id}`,
};
try {
const _res = await axios.get(`${URL}/auth/profile/${id}`);
return _res.data;
} catch (e) {
throw e;
}
};
따라서 Promise interface의 예상 반환값의 타입, 즉 UserInfo를 제네릭으로 넘겨주면
우리가 원하는 형태로 타입 추론이 이뤄짐을 확인할 수 있다.
'source-code > TypeScript' 카테고리의 다른 글
"'React'은(는) UMD 전역을 참조하지만 현재 파일은 모듈입니다" 에러 해결 (2) | 2023.11.18 |
---|---|
getElementsByClassName에 for each 사용해 HTMLElement 접근하기 (0) | 2023.11.16 |
react-hook-form TS와 똑똑하게 사용하기 (0) | 2023.08.21 |
index signature 관련 타입 에러 (0) | 2023.08.21 |
JsToTs : axios (0) | 2023.08.21 |