[TypeScript] 타입을 파라미터로 입력하는 Generic

2022. 12. 30. 17:01TypeScript

⚡️ Generic이란?

타입을 인자로 받아서 타입을 리턴하는 함수

 

⚡️함수 return 값의 타입이 불확실 할 때

예를 들어

1. 아무렇게나 생긴 array 자료를 입력하면

2. array의 첫 자료를 그대로 출력해주는 함수를 만들었다고 합시다. 

function 함수(x: unknown[]) {
  return x[0];
}

let a = 함수([4,2])
console.log(a) 
console.log(a + 1)

1. 이러면 콘솔창에 4가 출력됩니다. 

2. 마우스 올려서 a의 타입을 확인해보면 숫자는 아니고 unknown 타입입니다.

3. 지금 입력하는 array도 unknown 타입이라서 그렇습니다. 

   ( 여기서 중요포인트는 타입스크립트는 타입을 알아서 변경해주지 않습니다.  )

4. 함수의 return 타입지정을 :number 이런 걸로 강제로 바꾸기 전까지는 number 타입으로 변하지 않습니다.

함수에 불확실한 unknown, any, union 타입을 입력하면
나오는 값도 unknown, any, union 타입이고, 이 때문에 일어나는 문제들이 많습니다. 
예를 들면 "함수가 10을 return 하는데 타입이 unknown 이라서 맘대로 조작을 못하네" 문제요  

 

해결책

1. narrowing 잘 하면 해결됩니다. 

2. 처음부터 타입을 파라미터로 함수에 미리 입력하는 방법도 있습니다. 그럼 원하는 곳에 가변적으로 타입지정 가능 

  ( 2번을 Generic 이라고 부릅니다. )

 


⚡️Generic 적용한 함수만들기 

함수에 <> 이런 괄호를 열면 파라미터를 또 입력할 수 있습니다. 

근데 여기 안엔 타입만 입력할 수 있습니다. ( 타입파라미터 문법 )

function 함수<MyType>(x: MyType[]) :MyType {
  return x[0];
}

let a = 함수<number>([4,2])
let b = 함수<string>(['kim', 'park'])

1. 함수를 사용할 때도 <> 안에 파라미터처럼 타입을 입력할 수 있습니다.

2. 함수<number>( ) 이렇게 쓰는 순간 

3. MyType 이라는 변수에 number 라는게 들어간다고 보시면 됩니다. 

4. 함수( x :number[] ) :number{ } 이거랑 똑같이 동작합니다. 

 

결론 : Generic을 쓰면 정한 타입을 return 값으로 뱉는 함수를 제작가능한 것입니다.

<> 문법만 잘 쓰면 됩니다. 

function 함수<MyType>(x: MyType[]) :MyType {
  return x[0];
}

let a = 함수([4,2])
let b = 함수(['kim', 'park'])

실은 함수 사용시 꼭 <> 안써도 알아서 기본 타입을 유추해서 집어넣어줍니다. 

이래도 결과는 똑같습니다. 

 

(참고)

- 타입파라미터는 자유작명가능 보통 <T> 이런걸로 많이 합니다. 

- 일반 함수파라미터 처럼 2개 이상 넣기도 가능합니다  

 


⚡️근데 왜 - 1은 불가능함 

Generic을 적용한 함수를 만들었는데 왜 에러가 날까요?

function 함수<MyType>(x: MyType) {
  return x - 1
}

let a = 함수<number>(100)

<MyType> 이라는 곳에 number 말고도 다른거 혹시 집어넣을 수 있으니까 저런 - 1 연산을 미리 방지해주는 것입니다. 

그래서 해결책은 narrowing을 하셔도 되는데 MyType에 집어넣을 수 있는 타입을 미리 제한하는 것도 하나의 해결책입니다. 

 

⚡️Generic 타입 제한하기 (constraints)

1. extends 문법을 쓰면 넣을 수 있는 타입을 제한할 수 있습니다. 

2. MyType extends number 라고 쓰면 타입 파라미터에 넣을 수 있는 타입을 제한가능합니다. 

3. interface 문법에 쓰는 extends와는 다른 느낌입니다.

4. 그 extends는 복사인데 이번 extends는 number와 비슷한 속성을 가지고 있는지 if 문으로 체크하는 문법이라고 보면 됩니다.

function 함수<MyType extends number>(x: MyType) {
  return x - 1
}

let a = 함수<number>(100) // 99

이러면 에러없이 잘됩니다. 

return 타입지정을 안한 이유는 숫자 - 숫자를 했으니 알아서 number 타입이 됩니다.

 


⚡️언제나 커스텀 타입도 extends 가능

예를 들어서 문자로 파라미터를 넣으면 자릿수를 세어서 출력해주는 함수를 Generic으로 만들고 싶습니다.

function 함수<MyType>(x: MyType) {
  return x.length
}

let a = 함수<string>('hello')

1. 문자에 .length 붙이면 몇자리의 문자인지 출력해주는데 에러나고 안됩니다.

2. 왜냐하면 MyType에 string을  넣었지만 나중에 number 실수로 넣을수 있어 아직 .length같은 조작을 일단 방지 해주는 것입니다.

3. 그래서 MyType을 extends 로 정확히 제한해주면 됩니다.

  ( 이번엔 interface로 만들어둔 타입을 extends 해봅시다. )

interface lengthCheck {
  length : number
}
function 함수<MyType extends lengthCheck>(x: MyType) {
  return x.length
}

let a = 함수<string>('hello')  //가능
let a = 함수<number>(1234) //에러남

1. length 속성을 가지고 있는 타입을 하나 만들었습니다. 이름은 lengthCheck로 했습니다. 

2. 그걸 extends 해주면 MyType도 length 속성을 복사해서 가집니다. 

3. 그래서 MyType은 length가 분명히 있기 때문에 맘대로 MyType을 부여받은 x는 .length 조작이 가능합니다.