infer는 타입스크립트의 조건부 타입(Conditional Types)과 함께 사용되어 특정 타입을 추론할 수 있는 기능을 가진다.
평소에 infer만 보면 쫄았는데, infer가 무엇인지 확실히 알아보자.
이런 귀여운 녀석이 있다. GetDatValue
는 말그대로 data
하위 객체 정보를 타입으로 가져오는 역할을 한다.
예를들어 {data: "hello"}
일때, 추출된 타입이"hello"
를 충족시키고 싶다. 그럼 테스트를 만족시키면서 타입을 정해보자
가장 간단하게는 extends
를 사용하여 { data : any}
를 확인하는 방법이 있을 수 있다.
이것도 좋지만 여기서 infer
를 사용하면 좀 더 명확해진다.
infer
로 추론되는 TData
는 조건식 스코프에서만 작동한다.
가령 삼항식에 false 부분에 TData
를 두면 스코프 에러가 나온다.
그럼, interface
를 사용하는 경우에서 infer
를 알아보자.
다음과 같은 MyComplexInterface
라는 인터페이스가 있다. 위와 같은 상황에서 getPoint
의 타입만 추출하고 싶다면 어떻게 해야할까?
GetPoint
를 extends하여 손봐보도록하자.
먼저 GetPoint
에 MyComplexInterface
를 extends하여 조건부로 만든다.
any
로 추론되고 있다. 4번째 항에 {x: 12, y: 14}
를 추론하고 싶으니 무언가를 해줘야 한다.
ReturnType
과 인덱스 타입을 적용하면
원하는 타입을 추론할 수 있다.
하지만, 이것으로는 부족하다. infer
를 사용하여 제네릭하게 변경해보자.
아주 추론이 잘 되고 있다. infer
와 조금 가까워진 것 같다
그럼 infer
로 어디까지 할 수 있는지 보자..
tuple 형태의 Names
와 성만 추출하는 GetSurname
이라는 타입이 있다.
GetSurname
통해서 성만 추출하는 타입을 작성해보자.
일단 조건식은 대략 이럴 것이다. 이제 "이름 성"
으로 이루어져있는지 확인을 하고 그렇다면 성을 반환하도록하면 된다.
여기서 template literal
과 infer
가 쓰인다.
${infer First} ${infer Last}
이부분을 template literal로 형태를 잡아주고 infer로 추론하는 것이다
모든 테스트가 다 통과된다.
(아니 infer로 이거까지 한다고...? )
그럼 이제 좀 더 실무적인 예제를 보자.
서버사이드에서 호출하는 api가 있다고 가정하자.
이제 InferPropsFromServerSideFunction
을 통해 함수의 props
리턴 타입을 추출해보자.
처음엔 이런 형태로 작성할 수 있을 것이다. () => Promise<any>
로 확장하여 any
를 반환한다.
이제 여기에 props
를 infer 한다.
테스트를 통과했다.
갈 때까지 간 infer의 마지막 예제다
다양한 형태의 파싱 함수가 있다.
GetParserResult
라는 하나의 유틸 함수를 사용하여 각 함수의 리턴값을 추출하는 것
먼저 노가다와 삼항으로 이렇게 작성할 수 있겠다. 모든 경우를 조건부에 적용시켰다.
하지만 가독성이 매우 떨어진다. 이것을 조금 이쁘게 리팩토링해보자. 이때 union
을 활용하면 된다.
3가지 타입 형태를 유니언으로 두고, 삼항을 사용하여 infer한 것
테스트는 모두 통과된다!
이제 마지막이다!! infer 정말 많이 쓰이는구나
Fruit
이라는 유니언 타입이 있다.
여기서 특정 타입만 골라서 새로운 타입을 만들고 싶을 때면 유니언을 extends해서 사용하면 될까?
AppleOrBanna
의 타입이 never
가 나와버린다.
이유인즉슨, Fruit
타입 전체('apple' | 'banana' | 'orange')가 'apple' | 'banana'의 서브타입인지 검사하기 때문이다.
여기서 분배 조건부 타입(Distributive Conditional Types)으로 해결할 수 있다.
타입스크립트에서 제네릭을 사용하게 되면 T
를 분리하여 유니언 개별 타입에 적용한다.
개별 타입에 대한 T
이므로 삼항은 정상 동작하게 된다.
(AppleOrBanna에서 GetAppleOrBanna로 변수명 변경)
이제 타입이 제대로 작동하는 것을 확인할 수 있다.
여기서 제네릭을 사용하지 않고 infer를 사용해서도 가능하다.
(거의 포기)
Fruit extends infer T
로 Fruit
유니온 타입('apple' | 'banana' | 'orange')을 infer T
로 분배한다.
제네릭으로 했던 것과 동일하게 T extends "apple" | "banana"
에서 개별 타입으로 검사한다.
이렇게 되면 동일하게 타입이 작동한다. (오늘 ts 여기까지...)
참고 : total typescript