기록을 불러오는 중입니다...
function useFooBar() {
// 초기값은 null로 설정!
const [fooResult, setFooResult] = useState<AxiosResponse | null>(null);
const [barResult, setBarResult] = useState<AxiosResponse | null>(null);
// 각 axios 결과가 200 이어야 한다.
const isUploadingFooSuccess = fooResult?.status === 200;
const isUploadingBarSuccess = barResult?.status === 200;
// BE 에 notify 요청을 보낼 수 있는 조건
const isCanNotify = isUploadingFooSuccess && isUploadingBarSuccess;
return {
// ...
isCanNotify
};
}
useFooBar 커스텀 훅에서는 nullable 상태인 fooResult 와 barResult 를 가지고 있으며, 이 두 값 모두 status 속성이 200 이라면 notify 를 할 수 있다.function UploadFooBar() {
const {
isCanNotify,
} = useFooBar();
const uploadFooBarForPreProcess = () => {
// isCanNotify 를 이용해 early return 적용!
if (!isCanNotify) {
showErrorToast(new Error('전처리에 필요한 파일이 정상적으로 업로드되지 않았습니다. 다시 업로드 해주세요.'));
return;
}
// 이 시점부터 fooResult 와 barResult 는 non-null 이다.
Promise.all([
notifyFoo(fooResult.config.data.name), // TS2531: Object is possibly 'null'.
notifyBar(barResult.config.data.name), // TS2531: Object is possibly 'null'.
])
// ...
};
return (
<form onSubmit={uploadFooBarForPreProcess}>
/* ... */
</form>
);
}
isCanNotify 를 통해 개발자는 fooResult 와 barResult 가 null 이 아님을 알고 있지만, 타입가드를 해주지 않았기 때문에, TS는 그 사실을 알지 못해서 타입 에러가 발생!function useFooBar() {
// 두 상태 모두 사용할 수 있는 범용 타입 가드 함수 구현
function isUploadingFooBarSuccess<T extends AxiosResponse>(
fooBarResult: T | null,
): fooBarResult is Exclude<T, null> {
return fooBarResult?.status === 200;
}
// T | null 타입의 튜플을 받는 타입 가드 함수로 변경.
// 만약 타입 가드를 통과한다면, 인자가 [T, T] 타입임을 보장한다.
function isCanNotify<T extends AxiosResponse>(args: [T | null, T | null]): args is [T, T] {
return args.every(isUploadingFooBarSuccess);
}
// ...
}
isUploadingFooBarSuccess 라는 범용 함수를 구현하고, isCanNotify 또한 타입 가드 함수로 바꿨다.args 를 [T | null, T | null] 이라는 튜플 타입으로 정의하고, 반환 시그니처를 args is [T, T] 로 설정하여 반환된 튜플의 원소가 모두 T 임을 보장하게 했다.function UploadFooBar() {
const uploadFooBarForPreProcess = () => {
// isCanNotify 타입 가드의 인자로 넣기 위한 변수를 선언 및 할당
const uploadingFooBarList: [AxiosResponse | null, AxiosResponse | null] = [
fooResult,
barResult,
];
// 타입 가드!
if (!isCanNotify(uploadingFooBarList)) {
showErrorToast(new Error('전처리에 필요한 파일이 정상적으로 업로드되지 않았습니다. 다시 업로드 해주세요.'));
return;
}
// 타입 에러가 발생하지 않는다!
Promise.all([
notifyFoo(uploadingFooBarList[0].config.data.name),
notifyBar(uploadingFooBarList[1].config.data.name),
])
// ...
};
// ...
}
uploadingFooBarList 라는 튜플 변수를 만든 뒤, isCanNotify 를 통해 타입 가드를 처리한다.uploadingFooBarList 의 타입이 [AxiosResponse, AxiosResponse] 로 추론되어, 인덱스를 통해 원소를 뽑아오면 타입 에러가 발생하지 않는다.