타입 시스템의 큰 장점 중 하나는 데이터 타입을 명확히 알 수 있어 코드를 이해하기 쉽다는 것이다.
4장(타입 설계)에서는 타입 자체의 설계에 대해 다룬다.
대부분 다른 언어에서도 적용될 수 있는 아이디어다.
아이템 28. 유효한 상태만 표현하는 타입을 지향하기 #
효과적으로 타입을 설계하려면, 유효한 상태만 표현할 수 있는 타입을 만들어 내는 것이 중요합니다.
interface State {
pageText: string;
isLoading: boolean;
error?: string;
}
위 타입이 페이지를 렌더링하는 상태를 나타낸다고 가정할 때, 이 타입은 애매하다. (모호하다.)
즉 잘못된 타입(설계)이다.
어떻게 사용하느냐에 따라 다를 수도 있을 것 같다.
예를 들어 다음과 같은 코드가 있다.
function renderPage(state: State) {
if(state.error) {
// 에러 ...
} else if(state.isLoading) {
// 로딩 중...
} else {
// 정상 ...
}
}
위 코드에서 만약 ’error 값이 있고’, isLoading 이 true 라면?
error 분기를 타겠지만 이건 의도한건가?
이런 관점에서 이 타입은 명확하지 않다.
아래와 같이 개선해볼 수 있다.
interface RequestPending {
state: 'pending';
}
interface RequestError {
state: 'error';
error: string;
}
interface RequestSuccess {
state: 'ok';
pageText: string;
}
type RequestState = RequestPending | RequestError | RequestSuccess
interface State {
currentPage: string;
requests: {[page: string]: RequestState};
}
function renderPage(state: State) {
const {currentPage} = state;
const requestState = state.requests[currentPage];
switch(requestState.state) {
case 'pending':
// pending ...
case 'error':
// error ...
case 'ok':
// ok ...
}
}
요청에 대한 각각의 상태를 명시적으로 모델링하는 태그된 유니온(또는 구별된 유니온) 을 사용했다.
이 상태는 무효한 상태를 허용하지 않는다.
각각의 상태에서 발생할 수 있는 요청의 모든 상태를 표현한다.
이후 책에서 설명하는 에어프랑스 447 항공편 에어버스 이야기가 재밌다.
유효한 상태를 표현하는 값만 허용한다면 코드를 작성하기 쉬워지고 타입 체크가 용이해진다. #
타입을 설계할 때는 어떤 값들을 포함하고, 어떤 값들을 제외할지 신중하게 생각해야 한다.
유효한 상태를 표현하는 것에 집중하자.
요약 & 정리 #
- 유효한 상태와 무효한 상태를 둘 다 표현하는 타입은 혼란을 초래하기 쉽다. (+ 오류를 유발하기 쉽다.)
- 유효한 상태만 표현하는 타입을 지향해야 한다.
- 코드가 길어지거나 표현하기 어려울 수 있다.
- 하지만 결국에는 시간을 절약하고 고통을 줄일 수 있다.