조건부 타입(삼항연산자와 동일): Condition ? A : B 형태.
Condition이 true면 A, 아니면 B를 반환.
조건부 타입의 장점
: 중복 코드를 제거하고 상황에 맞는 타입을 설정 가능.
: 더 정확한 타입 설계에 도움.
.
.
.
T extends U ? X : Y
T가 U를 확장하면 X, 아니면 Y를 반환.
interface Bank {
financialCode: string;
companyName: string;
name: string;
fullName: string;
}
interface Card {
financialCode: string;
companyName: string;
name: string;
appCardType: string;
}
type PayMethod<T> = T extends "card" ? Card : Bank;
type CardPayMethodType = PayMethod<"card">; // Card 타입 반환
type BankPayMethodType = PayMethod<"bank">; // Bank 타입 반환
extends와 조건부 타입 활용 시, 특정 조건에 따라 이 인터페이스를 포함하거나 제외 가능.
:중복된 코드가 발생하고 API 엔드포인트 관리가 어려움.
( API 엔드포인트 )
/baeminpay/.../bank → 은행 정보 반환.
/baeminpay/.../card → 카드 정보 반환.
/baeminpay/.../appcard → 앱 카드 정보 반환.
API 엔드포인트: 계좌, 카드, 앱카드의 결제 수단 정보를 배열 형태로 반환.
API 엔드포인트가 비슷하기 때문에 서버 응답 처리 공통 함수 생성
-> 해당 함수에 타입을 전달 - 타입별로 처리 로직 구현
React Query와 조건부 타입
useQuery
를 사용한 커스텀 훅:useGetRegisteredList
-> useQuery의 반환 값을 돌려줌.
fetcherFactory
: Axios를 래핑해주는 함수.
서버에서 데이터를 가져오고 onSuccess
콜백 함수를 거친 결곽값을 반환.
type PayMethodType = PayMethodInfo<Card> | PayMethodInfo<Bank>;
export const useGetRegisteredList = (
type "card" | "appcard" | "bank"
): UseQueryResult<PayMethodType[]> => {
const url = `baeminpay/codes/${type === "appcard" ? "card" : type}`;
const fetcher = fetcherFactory<PayMethodType[]>({
onSuccess: (res) => {
const usablepocketlist =
res?.filter(
(pocket : PocketInfo<Card> | PocketInfo<Bank>) =>
pocket?.useType === "USE"
) ?? [];
return usablePocketList;
},
});
const result = useCommonQuery<PayMethodType[]>(url, undefined, fetcher);
return result;
};
useGetRegisteredList
type에 따라 데이터를 반환:
"card"
,"appcard"
, "bank"
-> 해당 결제 수단의 결제 수단 정보 리스트를 반환.useGetRegisteredList
가 반환하는 Data
타입은 PocketInfo<Card> | PocketInfo<Bank>
타입 설계가 유연하지 않아(유니온으로 되어있어서),
타입스크립트가 전달된 데이터에 따라 반환 타입(Data)을 명확히 추론하지 못함.
-> 결론 : 인자에 다라 반환되는 타입을 다르게 설정, extends를 사용한 조건부 타입을 활용 : 반환 타입을 동적으로 설정.
type PayMethodType<T extends "card" | "appcard" | "bank"> = T extends
| "card"
| "appcard"
? Card
| Bank;
결제 수단 타입 : "card", "appcard", "bank"만 들어올 수 있기 때문에 extends를 한정자로 활용 -> 제네릭에 넘겨오는 값 제한
외의 값 입력 시 컴파일 에러 발생.
타입 안정성 향상:
useGetRegisteredList("card")
→ PocketInfo 타입 자동 추론.
useGetRegisteredList("bank")
→ PocketInfo 타입 자동 추론.
조건부 타입을 활용하면 가독성 및 유지보수성 개선:
중복 코드 제거.
-> API 변경 시 조건부 타입만 수정하면 전체 코드 반영 가능.
extends 활용 예시
그러면, extends로 조건을 서술 -> infer로 타입을 추론
typescript에서는 유니온 타입을 사용 -> 변수 타입을 특정 문자열로 지정
type HeaderTag = "h1" | "h2" | "h3" | "h4" | "h5";
컴파일타입의 변수에 할당되는 타입을 특정 문자열로 정확하게 검사 -> 휴먼 에러 방지
자동완성기능 -> 개발 생산성을 높일 수 있음.
type HeadingNumber = 1 | 2 | 3 | 4 | 5;
type HeaderTag = `h${HeadingNumber}`;
주의 ) 타입스크립트 컴파일러가 유니온을 추론하는데 시간이 오래걸리면 비효율적
-> 타입스크리브가 타입을 추론하지 않고 에러를 내뱉을 때가 있음.
결론 : 템플릿 리터럴 타입에 삽입된 유니온 조합의 경우의 수가 너무 많지 않게 적절하게 나누어 타입을 정의하는 게 좋다.
.
.
.
도서참조 : 우아한 타입스크립트 with 리액트