TypeScript

[TypeScript]타입스크립트 제네릭 함수(Generic Function)

DevStory 2021. 9. 14.

제네릭(Generic)은 C#과 Java와 같은 객체지향 프로그래밍에서 사용하는 기법입니다. 

 

메서드 매개변수의 구체적인 타입을 기재하지 않고 다양한 타입을 처리할 수 있는 기술이며, 제네릭을 잘 사용한다면 코드의 재사용성을 높일 수 있습니다.

 

이러한 제네릭은 타입을 엄격하게 처리하는 TypeScript에서도 사용할 수 있으며, 함수, 인터페이스, 클래스의 재사용성을 높일 수 있습니다.

 

이번 포스팅에서는 타입스크립트에서 제네릭 기법을 사용하여 함수를 구현하는 방법에 대해 정리하였으며, 이론적으로 설명하기에는 한계가 있어서 코드 위주로 정리하였습니다.

 


제네릭(Genric)

다음은 매개변수를 그대로 반환하는 함수들입니다.

// number 타입의 매개변수를 return하는 함수
function NumberReturnFunc(arg: number): number {
  return arg;
}

// string 타입의 매개변수를 return하는 함수
function StringReturnFunc(arg: string): string {
  return arg;
}

// boolean 타입의 매개변수를 return하는 함수
function BooleanReturnFunc(arg: boolean): boolean {
  return arg;
}

함수의 기능은 똑같은데, 매개변수의 타입과 반환하는 타입이 다르다는 이유로 여러 개의 함수를 구현하였습니다.

 

이러한 방식은 제네릭 기법을 사용하여 한 개의 함수로 구현할 수 있습니다.

function GenericReturnFunc<T>(arg: T): T {
  return arg;
}

제네릭 함수 구현 방법은 함수명 뒤에 <T>를 추가하며, T를 매개변수의 타입 또는 반환 타입으로 설정할 수 있습니다.

 

꼭 <T>로 작성할 필요는 없으며, T 대신에 다른 문자열을 사용해도 됩니다.

(인터넷 또는 책에서 대부분 T로 작성하는 이유는 T가 Type의 약자이기 때문입니다.)

 

아래는 위에서 만든 제네릭 함수를 호출하는 코드입니다.

let numVar = GenericReturnFunc<number>(123);
let strVar = GenericReturnFunc<string>('ABC');

<> 안에는 인수의 타입을 작성합니다.

 

만약, 인수와 인수의 타입이 다를 경우 아래 사진처럼 경고문이 발생합니다.

<> 안에 작성한 타입과 제네릭 함수에 전달하는 인수의 타입이 일치한지 확인할 수 있습니다.

반응형

any 타입의 문제점

문제점 1. any 타입은 함수의 반환 타입을 유추하기 어려움

위에서 설명한 내용은 제네릭 함수를 사용하지 않고 아래 코드처럼 any 타입으로 매개변수의 타입과 함수의 반환 타입을 지정할 수 있습니다.

function AnyReturnFunc(arg: any): any {
  return arg;
}

 

하지만, any 타입을 함수의 반환 타입으로 지정한 경우 무슨 값을 반환하는지 유추하기 어렵습니다.

// number 타입의 값인 123을 전달하였지만, 무슨 타입을 return 받는지 유추하기 어려움
let numVar = AnyReturnFunc(123);

// stirng 타입의 값인 ABC를 전달하였지만, 무슨 타입을 return 받는지 유추하기 어려움
let strVar = AnyReturnFunc('ABC');

 

함수의 반환 타입을 any로 명시하는 것보다 <> 안에 타입을 명시하여 무슨 타입으로 반환하는지 유추하기 쉬운 제네릭 함수를 사용하는 것이 유지보수 측면에서 좋습니다.

function GenericReturnFunc<T>(arg: T): T {
  return arg;
}

let numVar = GenericReturnFunc<number>(123);

 

참고로 제네릭 함수를 호출할때, <> 안에 타입을 명시하는 것이 필수는 아닙니다. 아래 코드처럼 <>를 생략할 수 있는데요.

let strVar = GenericReturnFunc('ABC');

<>를 생략하는 경우 컴파일러가 인수의 타입을 보고 타입을 결정합니다.

 

'ABC'는 문자열(string)이므로 컴파일러는 인수의 타입이 문자열이므로 T는 string으로 결정합니다.

 

코드를 간결해주는 장점이 있지만, 타입을 유추하기 어려운 경우에는 <> 안에 타입을 명시하는 것이 좋습니다.

 

문제점 2. any 타입은 매개변수의 프로퍼티를 체크하지 않음

매개변수의 lentgh를 반환하는 코드입니다.

function AnyReturnFunc(arg: any): any {
  return arg.length;
}

function GenericReturnFunc<Type>(arg: Type): Type {
  return arg.length;
}

타입이 any인 코드는 경고가 없지만, 제네릭 함수는 사진처럼 경고가 발생합니다.

 

 

제네릭 함수는 무슨 타입이 올지 모르기 때문에 length 프로퍼티를 사용할 수 없습니다.

 

만약, any 타입으로 작성된 함수를 사용했을 경우 프로그램 실행 도중에 문제가 발생할 가능성이 많지만, 제네릭 타입을 사용하는 경우 사전에 문제가 되는 부분을 미리 방지할 수 있다는 장점이 있습니다.


제네릭 화살표 함수(Generic Arrow Function)

제네릭 화살표 함수를 아래 코드처럼 구현하는 경우 에러가 발생합니다.

let GenericReturnFunc = <Type>(arg: Type): Type => {
  return arg;
}

.ts 확장자 파일에서는 정상적으로 동작할 수 있는데, .tsx 확장자 파일은 TypeScript + JSX로 구성되어 있어서 <Type>에서 태그(<>) 문제가 발생합니다.

 

.tsx 확장자 파일에서 제네릭 화살표 함수를 구현해야 하는 경우 제네릭 매개변수에 extneds를 사용하여 컴파일러에게 제네릭 화살표 함수라고 알려줘야 합니다.

let GenericReturnFunc = <Type extends {}>(arg: Type): Type => {
  return arg;
}
반응형

댓글