발견한 에러
위도와 경도, Google Map API를 사용해 현재 유저의 위치를 반환하는 역지오코딩 함수 FetchAddress를 만들었다.
const FetchAddress = () => {
return new Promise((resolve, reject) => {
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(
position => {
const { latitude, longitude } = position.coords;
const apiKey = import.meta.env.VITE_Google_API_KEY;
fetch(
`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=${apiKey}&language=en`
)
.then(response => response.json())
.then(data => {
if (data.status === 'OK') {
const addressData: string = data.results[4].formatted_address;
const commaIndex = addressData.indexOf(',');
const secondCommaIndex = addressData.indexOf(
',',
commaIndex + 1
);
const addressResult = addressData.substring(
0,
secondCommaIndex
);
resolve(addressResult);
이 때 TanStack Query로 해당 데이터를 받아오고, 데이터를 받아오는 동안 로딩 메세지를 띄우거나 에러가 발생할 경우 에러 메세지를 띄우게 하는 코드를 작성했다.
const Home = () => {
const { data, isPending, isError } = useQuery({
queryKey: ['address'],
queryFn: FetchAddress,
});
let content;
if (isPending) {
content = <div>Loading...</div>;
}
if (isError) {
content = <div>error</div>;
}
if (data) {
content = <div>{data}</div>;
}
아니나 다를까 마지막 if문의 <div>{data}</div>의 data에 빨간 밑줄이 그어졌다.
Type '{}' is not assignable to type 'ReactNode'.ts(2322)
index.d.ts(2379, 9): The expected type comes from property 'children' which is declared here on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'
타입 {}는 'ReactNode' 타입에 할당할 수 없다.
예상되는 타입은 여기에 선언된 'children' 속성에서 오는데, 이 속성은 'DetailedHTMLprops<HTMLAttributes<HTMLDivElement>, HTMLDivElement>' 유형에 선언되어 있다고 한다.
index.d.ts를 클릭해보니 이 부분이 보인다.
DOMAttributes<T> 인터페이스의 children 속성은 ReactNode 또는 undefined 유형을 허용하도록 정의되어 있다.
이 때 ReactNode에 호버해보면 'type React.ReactNode = string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | null | undefined'라고 한다.
즉, JSX 요소에서 자식으로 사용될 수 있는 모든 유형의 데이터를 포함한다는 뜻으로 해석된다.
타입 {}는 ReactNode에 할당할 수 없다고 했다.
FetchAddress에서 {}를 반환하는 경우는 위치 정보가 존재하지 않거나 네트워크 문제 등 어떤 에러를 만났을 때라고 추측된다.
어떤 분이 도움을 주셔서 찾아본 TanStack Query 개념 저장소에서 발견한 내용이다.
사실 TanStack Query는 타입을 잘 전달하기 때문에 굳이 개발자가 명시해줄 필요가 없고, 사용하는 queryFn의 타입을 명시해주기만 하면 된다는 것이었다.
FetchAddress는 비동기로 이루어지기 때문에 Promise를 나타낸다.
에러가 발생한다면 먼저 catch문에서 unknown으로 잡힐 것이고, 그외의 경우는 문자열 값을 반환한다.
데이터가 성공적으로 들어오는 경우에도 문자열 값을 반환하기 때문에 FetchAddress의 값을 Promise<string>으로 명시해준다.
그러자 코드가 얌전해진 것을 확인할 수 있다.
이전에 2가지 방법을 생각했었다.
1번째는 타입 단언이다.
if (data) {
content = <div>{data as string}</div>;
}
하지만 타입 단언은 되도록 지양하는 게 좋다고 생각되어 제외했다.
2번째는 타입 좁히기다.
if (typeof data === 'string') {
content = <div>{data}</div>;
}
if (typeof data === 'string')으로 작성하면 타입 단언보다는 좀 안정적인 것처럼 보이지만 애초에 반환값을 명시해주는 게 더 휴먼 에러를 줄일 수 있고 번거롭지 않을 것 같아 제외했다.
결론
1. index.d.ts를 잘 읽어보자.
2. 타입 단언이나 타입 좁히기보다 타입 추론이 더 효율적이기 때문에, 애초에 함수의 반환값의 타입을 명시해주자.
3. 타입 스크립트 재미있다...
📌 참고
'❔ TypeScript' 카테고리의 다른 글
[TypeScript] Geolocation API와 구글 맵 API를 사용한 역지오코딩 (0) | 2024.05.28 |
---|---|
[TypeScript] axios error 객체 처리하기 (0) | 2024.04.29 |
[TypeScript] Object is possibly 'undefined' 에러 해결 (0) | 2024.03.21 |
[TypeScript] yarn + Vite + TS로 웹 개발 시작하기 (5) | 2024.03.07 |