[TypeScript] 조건문으로 타입만들기 & infer

2023. 1. 2. 17:15TypeScript

⚡️삼항연산자

자바스크립트 기본 문법 중에 삼항연산자라는게 있습니다.

if가 들어갈 수 없는 곳들에 간략하게 if문을 넣을 수 있는 방법입니다.

조건문 ? 참일때실행할코드 : 거짓일때실행할코드
3 > 1 ? console.log('맞아요') : console.log('아님')

 

⚡️조건부로 타입만들기

type Age<T> = T;

이러면 이제 Age<number> 이렇게 쓰면 그 자리에 number가 남습니다.

(타입변수에도 타입파라미터 넣기 가능)

 

if문을 사용하여 만약 T가 string이면 string, 그게 아니면 unknown 를 남기도록 하고싶다.

그럴때 타입 조건식은 주로 extends 키워드와 삼항연산자를 이용합니다.

"extends는 왼쪽이 오른쪽의 성질을 가지고 있냐" 라는 뜻으로 사용할 수 있기 때문에

조건식 용도로 사용가능합니다.

type Age<T> = T extends string ? string : unknown;
let age : Age<string> //age는 string 타입
let age2 : Age<number> //age는 unknown 타입

1. "T라는 파라미터가 string 성질 가지고 있냐? 그러면 string 남기고 아니면 unknown 남겨라"

2. <string> 넣으면 string, <number> 넣으면 unknown을 남겨줍니다.

3. 이게 if문 쓰는 방법입니다.

4. 아직 타입이 확실하지 않은 <타입파라미터> 다룰 때 많이 사용합니다.

 

⚡️infer 키워드

조건문에 사용할 수 있는 특별한 infer 키워드가 있습니다. 

infer 키워드는 지금 입력한 타입을 변수로 만들어주는 키워드입니다.

평상시에 사용하지 않지만 나오면 읽을 줄은 알아야하니 간단히 이해만 하길...

type Person<T> = T extends infer R ? R : unknown; 
type 새타입 = Person<string> // 새타입은 string 타입입니다 

1. infer 키워드는 조건문 안에서만 사용가능합니다.

2. infer 우측에 자유롭게 작명해주면 타입을 T에서 유추해서 R이라는 변수에 집어넣어라~ 라는 뜻입니다.

3. 그래서 위의 예제에서 <string> 이렇게 타입파라미터자리에 string 집어넣으면 R은 string이 됩니다.

4. R을 조건식 안에서 맘대로 사용가능합니다.

이런 식으로 타입파라미터에서 타입을 추출해서 쓰고싶을 때 쓰는 키워드라고 보시면 됩니다. 

 

⚡️infer 키워드 사용 용도

1. array 안에 있던 타입이 어떤 타입인지 뽑아서 변수로 만들어 때

type 타입추출<T> = T extends (infer R)[] ? R : unknown; 
type NewType = 타입추출< boolean[] > // NewType 은 boolean 타입입니다 

(infer R)[] 이렇게 하면 array가 가지고 있던 타입부분만 쏙 뽑아서 R 변수에 할당할 수 있습니다.

 

2. 함수의 return 타입이 어떤 타입인지 뽑아서 변수로 만들어 때

type 타입추출<T> = T extends ( ()=> infer R ) ? R : unknown; 
type NewType = 타입추출< () => number > // NewType은 number 타입입니다 

타입파라미터에 <함수>를 집어넣었습니다. 그 타입파라미터에 있는 return 타입을 뽑아서 R이라는 변수에 담는 코드입니다.

일정한 규칙이 있다기 보다 그냥 타입을 추출하는 식으로 이해하면 되겠습니다. 


(문제1) 타입 파라미터로 

1. array 타입을 입력하면

2. array의 첫 자료가 string이면 string 타입을 그대로 남겨주고 

3. array의 첫 자료가 string이 아니면 unknown 을 남겨주려면 어떻게 타입을 만들어놔야할까요?

 

(동작예시)

let age1 :Age<[string, number]>;
let age2 :Age<[boolean, number]>; 

이러면 age1의 타입은 string, age2의 타입은 unknown이 되어야합니다. (array나 tuple이나 그게 그거임)

이걸 만족하는 type Age를 만들어봅시다.

 

(정답)

type Age<T> = T extends [string, ...any] ? T[0] : unknown;
let age1 :Age<[string, number]>;
let age2 :Age<[boolean, number]>;

이러면 정말 age1은 string, age2는 unknown이 됩니다.

 

(문제2) 함수의 파라미터의 타입을 뽑아주는 기계를 만들어보십시오. 

타입뽑기<(x :number) => void> //이러면 number가 이 자리에 남음
타입뽑기<(x :string) => void> //이러면 string이 이 자리에 남음

아무튼 함수의 파라미터타입이 남아야합니다.

(정답)

type 타입뽑기<T> = T extends (x: infer R) => any ? R : any;
type a = 타입뽑기<(x :number) => void> 

이러면 a라는 타입이 number로 잘 남습니다.

참고로 함수만 들어올 수 있게 제한을 두고 싶으면

언제나 T 라는 함수 파라미터 만들 때 extends로 제한을 두면 됩니다.