1. 소개
이펙티브 타입스크립트에서 다음과 같이 Tagged Union Pattern을 시작하고 있다.
효과적으로 타입을 설계하려면, 유효한 상태만 표현할 수 있는 타입을 만들어 내는 것이 가장 중요합니다.
- 아이템28, p156 -
태그드 유니온 패턴은 타입에 태그를 추가하여 효율적으로 타입을 설계할 수 있는 방식이다. 이를 어디서 활용하면 좋을지 크게 두 가지로 나누어 정리해 보았다.
2. 사용
* 각 필드가 연관되어 특정될 경우
- 특정 페이지의 상태를 설계한다고 생각해보자. 아마 다음과 같은 타입의 상태를 정의할 수 있을 것이다
interface State {
pageText: string;
isLoading: boolean;
error?: string;
}
- 단순히 봤을 때는 문제가 없어 보인다. 그러나 페이지를 직접 렌더링하는 함수를 작성하게 되면, 우리는 에러가 있는지 없는지 체크를 해야하며, isLoading이 true인지 false인지를 체크해야 한다. 이렇게만 하더라도 2*2 가지의 상황이 생긴다. isLoading이 true일때 에러는 없음에도 불구하고 말이다.
- 만약 페이지를 전환하는 함수까지 추가된다면 이전 페이지의 상태까지 확인해야 하므로 더 많은 정보를 신경써야 한다. 따라서 명확하게 상태를 표현하려면 아래와 같이 각 상태를 연관이 있는 방향으로 특정되도록 설계할 필요가 있다.
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};
}
- 요청 과정의 상태를 태그드유니온을 사용하여 명시적으로 표현하였다. 이 때 태그는 state가 될 것이며, 유니온 타입으로 요청시의 상태(RequestState)라는 것을 명확하게 표현하였다.
- 이로써 렌더링 하는 함수는 훨씬 짧고 명확하게 switch문으로 구현할 수 있게되었다. 뿐만 아니라 State를 좀 더 상위의 개념으로 표현하면서 requests를 감싸서 표현하였다. 이를 통해 페이지를 전환하더라도 각 페이지를 구분지어 분별할 수 있게 된다.
유니온 타입의 속성을 가지는 인터페이스를 작성 중이라면, 혹시 인터페이스의 유니온 타입을 사용하는 게 더 알맞지는 않을지 검토해 봐야 합니다.
- 아이템32, p173 -
- 이번에는 조금 더 복잡하게 그림을 그리는 프로그램의 인터페이스를 생각해보자. 이는 유니온 타입의 속성을 여러 개 갖게된다.
interface Layer {
layout: FillLayout | LineLayout | PointLayout;
paint: FillPaint | LinePaint | PointPaint;
}
- 각 필드가 연관이 있다. 예를 들어 LineLayout은 LinePaint만 필요할 것이다. 따라서 이 경우도 위에서 봤던 예시처럼 각 상태를 필드가 연관이 있는 방향으로 특정되도록 설계하는 것이 추후 함수를 작성할 때 효과적이다.
- 태그를 사용한 태그드 유니온패턴을 사용한 코드는 다음과 같다.
interface FillLayer {
type: 'fill';
layout: FillLayout;
paint: FillPaint
}
interface LineLayer {
type: 'line';
layout: LineLayout;
paint: LinePaint;
}
interface PointLayer {
type: 'point';
layout: PointLayout;
paint: PointPaint;
}
type Layer = FillLayer | LineLayer | PointLayer;
- type 이라는 태그를 주었고, 이는 함수를 작성할 때 이 레이어가 어떤 레이어인지를 구분할 수 있게 해줄 것이다. 자연스럽게 타입스크립트는 타입에 맞는 레이아웃과 페인트를 유추할 수 있게 된다.
* 여러 개의 선택적 필드가 함께 있거나 없을 경우
- 위에서는 각 필드가 연관이 있었다면, 이번엔 각 필드가 동시에 존재하거나 없어야 하는 경우이다.
여러 개의 선택적 필드가 동시에 값이 있거나 동시에 undefined인 경우도 태그드 유니온 패턴이 잘 맞습니다.
- 아이템32, p176 -
- 간단한 예시로 사람을 나타내는 타입을 살펴보자.
interface Person {
name: string;
birth: Date;
school?: string;
studentId?: number;
}
- 위의 경우 school과 studentId는 함께 존재하거나 함께 없는 필드이다. 그럼에도 불구하고 이러한 정보를 알 수 없으며, 이는 코드를 작성할 때 실수를 할 여지를 남긴다. 이를 태그드 유니온 패턴을 사용한다면 어떻게 작성할 수 있을까
interface Person {
name: string;
birth: Date;
school?: {
name: string;
studentId: number;
}
}
- 위는 school을 마치 태그처럼 사용한 것이다. 이제 school필드가 있는지만 체크하더라도 학교이름과 학번을 가지는 객체라는 것이 명확해 진다.
- 하지만 위는 객체의 구조가 바뀌어야 하는 경우이다. 만약 객체의 구조를 손댈 수 없다면 어떨까. 이럴 때에는 인터페이스의 유니온을 사용할 수 있다.
interface Human {
name: string;
birth: Date;
}
interface Student extends Human {
school: string;
studentId: number;
}
type Person = Human | Student;
- 위처럼 작성하면 타입 코드만 읽더라도 관계가 명확해 진다. 코드를 작성할 때에도 school만을 태그로 판별하면 해당 객체가 Human인지 Student인지 체크할 수 있다.
참고
해당 포스팅은 이펙티브 타입스크립트를 읽고 개인적으로 필요한 내용을 추가 및 정리한 글입니다.
'Language > TypeScript' 카테고리의 다른 글
<TS> 타입가드 (0) | 2022.09.03 |
---|---|
<TS> 효과적으로 타입 명시하기 (0) | 2022.07.16 |
<TS> 유틸리티 타입으로 반복 줄이기 (0) | 2022.07.02 |