타입스크립트에서는 조건에 따라 다른 로직을 실행할 수 있다.
Condition ? true : false
삼항연산자를 활용한 조건부 로직
그렇다면, 타입도 조건에 따라 다르게 할당할 수 있을까?
예를들면, Condition이 true면 AType, false면 BType 처럼 말이다.
조건부 타입이 필요한 상황
타입에도 조건부 를 부여할 수 있다.
즉 , 조건부 타입이 가능하다.
현업에서 조건부 타입이 필요했던 상황과 활용방법에 대해 알아보자.
요구사항
요구사항은 버스와 지하철, 사무기관의 정보를 보여주는 화면을 개발하는 것이였다.
서버에서 내려주는 spec은 아래와 같았다.
type Bus = {
name: string
lat: number
lng: number
}
type Subway = {
name: string
line: string
lat: number
lng: number
}
type Government = {
name: string
lat: number
lng: number
}
`Subway` 만 제외하면 `name`, `lat`, `lng` field가 공통으로 내려온다.
api path는 아래와 같다.
// api path
const busApiPath = '/facility/bus'
const subwayApiPath = '/facility/subway'
const governmentApiPath = '/facility/government'
suffix 로 `/facility/~` 로 시작하며 포맷또한 동일하다.
당시에 나는 버스, 지하철, 사무기관의 정보를 가져오는 hook을 각각 만들었고, hook을 호출해 화면을 구현했다.
const bus = useGetBus() // Bus[] | undefind
const subway = useSubway() // Subway[] | undefind
const government = useGovernment() // Government[] | undefind
중복코드를 제거할 수 있지 않을까?
버스와 지하철, 사무기관은 api spec이 매우 유사해서 굳이 hook을 각각 만들 필요가 있나라는 생각이 들었다.
그래서 고안해 본 코드는 아래와 같다.
type Facility = 'bus' | 'subway' | 'government'
const useFacility = (type: Facility): Bus[] | Subway[] | Government[] | undefined => {
const data = useGet(`/facility/${type}`)
return data
}
type을 버스, 지하철, 사무기관으로 구분했다.
그리고, 사용자가 필요로 하는 데이터에 맞는 타입을 hook에 매개변수로 넘기면 그 타입에 맞는 api를 호출하고 리턴하도록 했다.
리턴하는 타입은 당연히 버스/지하철/사무기관 일 수 있으므로 유니온으로 결합했다.
const bus = useFacility('bus')
const subway = useFacility('subway')
const government = useFacility('government')
짠! 기존엔 세개의 hook(useGetBus, useGetSubway, useGetGovernment) 파일이 별도로 있었는데,
useFacility hook 하나만으로 중복코드 제거에 성공했다!
문제발생

하지만, 늘 그렇듯, 마음대로 되지 않는다.

애써 중복코드를 제거하기 위해 하나의 hook으로 호출할 수 있도록 고안했으나,
결국 Hook을 사용하는 쪽에서 해당 데이터의 타입을 좁혀가는 과정이 필요했다.
타입가드를 사용하자!
맞다. 타입가드를 사용하면 된다.
const isBus = (data:Bus[] | Subway[] | Government[] | undefined) : data is Bus[] => {
return type === 'bus'
}
const isSubway = (data:Bus[] | Subway[] | Government[] | undefined) : data is Subway[] => {
return type === 'subway'
}
const isGovernment = (data:Bus[] | Subway[] | Government[] | undefined) : data is Government[] => {
return type === 'government'
}
그러면 이와같은 타입명제 함수를 만들고 사용하는데...
애써 hook을 간추렸더니, 그걸 사용하려면 복잡하게 타입을 검사해야한다는 점이 아쉬웠다.
그리고 만약 하나의 타입이 또 추가된다면 ? 또 그 타입에 대한 타입명제 함수를 추가해줘야 한다.
이 상황을 해소시켜줄 수 있는 방법은 조건부 타입을 사용하는 것이다.
조건에 따라 타입을 제한하는 것이다.
조건부 타입 사용하기
서론이 길었다. 조건부타입은 아래처럼 활용한다.
type T<A> = A extends B ? OneType : TwoType
A가 B를 만족하면 T는 OneType이고, 아니면 TwoType이라는 의미이다.
예제 코드를 보자.
type Base = {
name: string
lat: number
lng: number
}
type FacilityObject<Type> = Type extends 'subway' ? Subway : Base
일단 버스와 사무기관은 타입이름만 다르지 프로퍼티들은 동일하기 때문에 `Base` 라는 이름으로 사용했다.
단, 지하철은 line 프로퍼티가 있는 상태이므로 Subway를 그대로 사용했다.
FacilityObjecy 타입은 Type이 subway면 Subway 타입, 그외면 Base타입이라는 의미이다.
const useFacility = <Type extends Facility>(type: Type): FacilityObject<Type>[] | undefined => {
...
}
그리고 이 타입을 앞서 만든 Hook에 사용하면,

이렇게 사용자가 넘긴 타입에 따라 자동으로 데이터 타입이 추론되는 것을 볼 수 있다.
결론
타입스크립트에서의 타입은 컴파일단계에서만 존재하기 때문에 런타임에서는 무용지물이다.
그래서 런타임환경에서 타입을 검사하기 위해서는 여러가지 타입 가드 방법을 통해 추론해나가야 한다.
조건부 타입도 그 추론방식 중 하나인데, 최근에야 알게 된 방법이라 아쉽다.
조금 더 빨리 알았더라면 더 좋은 코드를 짤 수 있지 않을까 싶다.
참고서적
우아한 타입 스크립트 with 리엑트 - 우아한 형제들
'🌳Frontend > typescript' 카테고리의 다른 글
Typescript 에서 string union type을 type guard 하는 법 (typeof Array[number] 와 as const) (0) | 2023.09.17 |
---|---|
[Typescript/TS] 배열에서 union type 만들기 (0) | 2023.07.13 |
[타입스크립트/Typescript] clean code 클린코드 (0) | 2023.03.21 |
타입스크립트에서는 조건에 따라 다른 로직을 실행할 수 있다.
Condition ? true : false
삼항연산자를 활용한 조건부 로직
그렇다면, 타입도 조건에 따라 다르게 할당할 수 있을까?
예를들면, Condition이 true면 AType, false면 BType 처럼 말이다.
조건부 타입이 필요한 상황
타입에도 조건부 를 부여할 수 있다.
즉 , 조건부 타입이 가능하다.
현업에서 조건부 타입이 필요했던 상황과 활용방법에 대해 알아보자.
요구사항
요구사항은 버스와 지하철, 사무기관의 정보를 보여주는 화면을 개발하는 것이였다.
서버에서 내려주는 spec은 아래와 같았다.
type Bus = {
name: string
lat: number
lng: number
}
type Subway = {
name: string
line: string
lat: number
lng: number
}
type Government = {
name: string
lat: number
lng: number
}
Subway
만 제외하면 name
, lat
, lng
field가 공통으로 내려온다.
api path는 아래와 같다.
// api path
const busApiPath = '/facility/bus'
const subwayApiPath = '/facility/subway'
const governmentApiPath = '/facility/government'
suffix 로 /facility/~
로 시작하며 포맷또한 동일하다.
당시에 나는 버스, 지하철, 사무기관의 정보를 가져오는 hook을 각각 만들었고, hook을 호출해 화면을 구현했다.
const bus = useGetBus() // Bus[] | undefind
const subway = useSubway() // Subway[] | undefind
const government = useGovernment() // Government[] | undefind
중복코드를 제거할 수 있지 않을까?
버스와 지하철, 사무기관은 api spec이 매우 유사해서 굳이 hook을 각각 만들 필요가 있나라는 생각이 들었다.
그래서 고안해 본 코드는 아래와 같다.
type Facility = 'bus' | 'subway' | 'government'
const useFacility = (type: Facility): Bus[] | Subway[] | Government[] | undefined => {
const data = useGet(`/facility/${type}`)
return data
}
type을 버스, 지하철, 사무기관으로 구분했다.
그리고, 사용자가 필요로 하는 데이터에 맞는 타입을 hook에 매개변수로 넘기면 그 타입에 맞는 api를 호출하고 리턴하도록 했다.
리턴하는 타입은 당연히 버스/지하철/사무기관 일 수 있으므로 유니온으로 결합했다.
const bus = useFacility('bus')
const subway = useFacility('subway')
const government = useFacility('government')
짠! 기존엔 세개의 hook(useGetBus, useGetSubway, useGetGovernment) 파일이 별도로 있었는데,
useFacility hook 하나만으로 중복코드 제거에 성공했다!
문제발생

하지만, 늘 그렇듯, 마음대로 되지 않는다.

애써 중복코드를 제거하기 위해 하나의 hook으로 호출할 수 있도록 고안했으나,
결국 Hook을 사용하는 쪽에서 해당 데이터의 타입을 좁혀가는 과정이 필요했다.
타입가드를 사용하자!
맞다. 타입가드를 사용하면 된다.
const isBus = (data:Bus[] | Subway[] | Government[] | undefined) : data is Bus[] => {
return type === 'bus'
}
const isSubway = (data:Bus[] | Subway[] | Government[] | undefined) : data is Subway[] => {
return type === 'subway'
}
const isGovernment = (data:Bus[] | Subway[] | Government[] | undefined) : data is Government[] => {
return type === 'government'
}
그러면 이와같은 타입명제 함수를 만들고 사용하는데...
애써 hook을 간추렸더니, 그걸 사용하려면 복잡하게 타입을 검사해야한다는 점이 아쉬웠다.
그리고 만약 하나의 타입이 또 추가된다면 ? 또 그 타입에 대한 타입명제 함수를 추가해줘야 한다.
이 상황을 해소시켜줄 수 있는 방법은 조건부 타입을 사용하는 것이다.
조건에 따라 타입을 제한하는 것이다.
조건부 타입 사용하기
서론이 길었다. 조건부타입은 아래처럼 활용한다.
type T<A> = A extends B ? OneType : TwoType
A가 B를 만족하면 T는 OneType이고, 아니면 TwoType이라는 의미이다.
예제 코드를 보자.
type Base = {
name: string
lat: number
lng: number
}
type FacilityObject<Type> = Type extends 'subway' ? Subway : Base
일단 버스와 사무기관은 타입이름만 다르지 프로퍼티들은 동일하기 때문에 Base
라는 이름으로 사용했다.
단, 지하철은 line 프로퍼티가 있는 상태이므로 Subway를 그대로 사용했다.
FacilityObjecy 타입은 Type이 subway면 Subway 타입, 그외면 Base타입이라는 의미이다.
const useFacility = <Type extends Facility>(type: Type): FacilityObject<Type>[] | undefined => {
...
}
그리고 이 타입을 앞서 만든 Hook에 사용하면,

이렇게 사용자가 넘긴 타입에 따라 자동으로 데이터 타입이 추론되는 것을 볼 수 있다.
결론
타입스크립트에서의 타입은 컴파일단계에서만 존재하기 때문에 런타임에서는 무용지물이다.
그래서 런타임환경에서 타입을 검사하기 위해서는 여러가지 타입 가드 방법을 통해 추론해나가야 한다.
조건부 타입도 그 추론방식 중 하나인데, 최근에야 알게 된 방법이라 아쉽다.
조금 더 빨리 알았더라면 더 좋은 코드를 짤 수 있지 않을까 싶다.
참고서적
우아한 타입 스크립트 with 리엑트 - 우아한 형제들
'🌳Frontend > typescript' 카테고리의 다른 글
Typescript 에서 string union type을 type guard 하는 법 (typeof Array[number] 와 as const) (0) | 2023.09.17 |
---|---|
[Typescript/TS] 배열에서 union type 만들기 (0) | 2023.07.13 |
[타입스크립트/Typescript] clean code 클린코드 (0) | 2023.03.21 |