FRONTENDSCHOOL6 / WonT

멋쟁이사자처럼 프론트엔드스쿨 6기 10조 "✈️여행 플래너" 프로젝트
https://weontrip.vercel.app
0 stars 3 forks source link

[Question] 유틸함수 `getPocketHostImageURL` JS -> TS 작업 중 item 데이터 타입 에러 발생 #180

Open uniS2 opened 9 months ago

uniS2 commented 9 months ago

😿 질문

설명

타입스크립트로 언어 변경을 위해 item의 data 타입을 LocalItem | MyScheduleItem | RecommenItem | TravelItem로 작성하였습니다. (Supabase API 각 데이터 record) 이때, item[image] 코드에서 밑과 같은 에러가 발생하였습니다.

'string' 형식의 식을 'LocalItem | MyScheduleItem | RecommenItem | TravelItem' 인덱스 형식에 사용할 수 없으므로 요소에 암시적으로 'any' 형식이 있습니다.
  'LocalItem | MyScheduleItem | RecommenItem | TravelItem' 형식에서 'string' 형식의 매개 변수가 포함된 인덱스 시그니처를 찾을 수 없습니다.ts(7053)
(parameter) image: string

wrtn을 통한 디버깅시 MyScheduleItem 에서 image 키값이 존재하지 않고 main, place, hotel 이 있기 때문이라 판단하였는데 any로 타입 선언 외에 해결 방법을 못찾고 있습니다. 이 외에 특정 타입을 지정하여 해결할 수 있는 방안이 있는지 논의하고자 질문합니다 🙇🏻‍♀️

문제 상황

현재 해결 방안

// getPocketHostImageURL.ts
import { LocalItem } from '@/types/Locals';
import { MyScheduleItem } from '@/types/MySchedule';
import { RecommenItem } from '@/types/Recommens';
import { TravelItem } from '@/types/Travels';

//$ 현재 해결 방안: any
//$ 에러 원인: LocalItem | MyScheduleItem | RecommenItem | TravelItem

const getPocketHostImageURL = (item: any, image: string = 'image') =>
  `${import.meta.env.VITE_PB_API}/files/${item.collectionId}/${item.id}/${
    item[image]
  }`;

시도한 해결 방법

1. item[fileName]타입 단언 ✖️

const getPocketHostImageURL = (
  item: LocalItem | MyScheduleItem | RecommenItem | TravelItem,
  image: string = 'image'
): string => {
  const imageUrl = item[image] as string; // 타입 단언을 사용합니다.
  return `${import.meta.env.VITE_PB_API}/files/${item.collectionId}/${item.id}/${imageUrl}`;
};

같은 에러 발생.

2. item[fileName] 유무 확인하여 경로 반환 ✖️

const getPocketHostImageURL = (
  item: LocalItem | MyScheduleItem | RecommenItem | TravelItem,
  fileName: string = 'image'
): string => {
  const filePath = item[fileName] ?? item.image;
  // const filePath = item.image ?? item[image];

  if (!filePath) {
    console.error('The item does not have the specified file property.');
    return '';
  }

  return `${import.meta.env.VITE_PB_API}/files/${item.collectionId}/${item.id}/${filePath}`;
};

'LocalItem | MyScheduleItem | RecommenItem | TravelItem' 형식에 'image' 속성이 없습니다. 'MyScheduleItem' 형식에 'image' 속성이 없습니다. 에러 발생.

hyeonjuuu commented 9 months ago

소이님! 이 방법이 맞는 방법인지는 모르겠으나 오류는 해결되었어요..! 아래 방법 참고해보세요 :)

import { LocalItem } from '@/types/Locals';
import { MyScheduleItem } from '@/types/MySchedule';
import { RecommenItem } from '@/types/Recommens';
import { TravelItem } from '@/types/Travels';

// #타입 정의
type ItemType = LocalItem | MyScheduleItem | RecommenItem | TravelItem;

const getPocketHostImageURL = <key extends keyof ItemType>(
  item: ItemType,
  image: key = 'image' as key
) =>
  `${import.meta.env.VITE_PB_API}/files/${item.collectionId}/${item.id}/${
    item[image]
  }`;

초기 문제는 각 타입이 동일하지 않은데 객체에 동적으로 접근해서 오류가 발생한 것 같습니다.

따라서 입력된 image 매개변수가 각 타입의 인터페이스의 키인 경우에만 해당 속성에 접근하도록 하는 것이 방법이라고 합니다! 위와 같이 수정하면 TypeScript는 image 매개변수가 각 인터페이스의 속성이 서로 다른 타입을 가질 수 있음을 알 수 있다고 하네요..!

저도 <key extends keyof ItemType>부분이 어떤 기능인지 잘 몰라서 WRTN으로 확인해 보았습니다! 아래 텍스트 참고해주세요..!

key extends keyof ItemType은 TypeScript의 제네릭과 인덱스 타입 쿼리를 사용한 것으로, 매우 강력한 기능입니다.

key: 이 부분은 제네릭입니다. 제네릭은 타입을 일반화시키는 방법으로, 여기서 K는 어떤 타입을 나타냅니다. 일반적으로 T를 많이 사용하지만, 여기서는 key(키)를 사용했습니다.
extends: 이 키워드는 제네릭 타입이 충족해야 하는 조건을 정의합니다. K는 extends 뒤에 오는 타입을 상속받아야 합니다.
keyof ItemType: 이 부분은 인덱스 타입 쿼리입니다. keyof 연산자는 객체 타입의 모든 속성 이름을 문자열 유니온 타입으로 추출합니다. 그래서 keyof ItemType은 ItemType 타입(여기서는 LocalItem | MyScheduleItem)의 모든 속성 이름을 나타냅니다.
따라서 key extends keyof ItemType의 의미는 key는 ItemType의 모든 속성 이름 중 하나의 타입이어야 한다는 것입니다. 이를 통해 key를 ItemType의 속성 이름으로 제한하고, 이를 함수의 매개변수로 사용하여 안전하게 객체의 속성에 접근할 수 있게 됩니다.