typescript

타입스크립트의 제네릭이란

하리하링웹 2024. 3. 7. 23:25

제네릭은 타입스크립트에서 재사용이 가능한 컴포넌트를 생성하는것을 도와주는 역할을 한다

문법

type A = <T>(a:T[]) => T

const a:A = (arr) => {
    return arr[0]
}

const result= a([1,2,3])
const result2 = a(['1','2'])
// 함수형
function A<SomeGeneric>(arg:SomeGeneric) {}

A<Number>
A<String>
A<OurType>

// 클래스형

// !Static과 같은 정적변수는 제네릭으로 관리할 수 없다.
class A<T> {
  constructor(a:T){}
}

new A<Number>(1)
new A<String>('a')

// 타입스크립트 인터페이스
interface GenericIdentityFn<Type> {
  (arg: Type): Type;
}

 

주의점

  • Static과 같은 정적변수는 제네릭으로 관리할 수 없다

  • 아래와 같이 사용 시 extends에 해당되는 타입을 사용하지 않으면 에러가 발생합니다.
interface Lengthwise {
  length: number;
}

function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
  console.log(arg.length); 
  return arg;
}

 

 

 

하나의 함수에 아래 코드처럼 여러개의 Generic 타입지정도 가능하다.

function getFirstElem<T,U>(arr:T[],arr2:U[]) {
  return arr[0]
}

예시

아래의 코드는 배열의 첫번째 인자를 반환해주는 코드이다. 제네릭을 사용하지 않는다면 아래와같이 모든 타입에 대해 함수를 만들어줘야만 타입 에러 없이 정상적으로 동작하게된다.

function getFirstNumElem(arr:number[]) {
  return arr[0]
}

function getFirstStrElem(arr:string[]) {
  return arr[0]
} 

const numArr = [1,2,3]
const firstNum = getFirstNumElem(numArr)

const stringArr = ['1','2','3']
const firstStr = getFirstStrElem(stringArr)

물론 아래와 같이 argument 타입을 any타입으로 만들어 하나의 함수로도 사용할 수 있게 만들어줄수있다

function getFirstElem(arr:any[]) {
  return arr[0]
}

// function getFirstStrElem(arr:string[]) {
//   return arr[0]
// } 

const numArr = [1,2,3]
const firstNum = getFirstElem(numArr)

const stringArr = ['1','2','3']
const firstStr = getFirstElem(stringArr)

하지만 이는 아래 이미지처럼 반환값 역시 any타입이 되는 문제가 발생하게된다.

 

 

함수를 이런식으로 바꾸면 되지 않을까?

 

function getFirstElem(arr:(number|string)[]) {
  return arr[0]
}

이러한 방법 역시 아래와 같은 문제가 발생한다.

 

 

혹은 argument의 타입이 늘어나면 늘어날수록 이러한 방식은 의미가 없어지게 된다.

이러할 때 사용할수 있는것이 타입스크립트의 제네릭 타입이다.

function getFirstElem<T>(arr:T[]) {
  return arr[0]
}

const numArr = [1,2,3]
const firstNum = getFirstElem<number>(numArr)

const stringArr = ['1','2','3']
const firstStr = getFirstElem<string>(stringArr)

위와 같이 사용 시 T를 Number 혹은 String과 같이 명시적으로 타입을 지정하여 return 타입 역시 해당 타입에 맞는 타입으로 할당되게된다.

 

 

 

물론 아래 코드와 같이 암시적으로도 generic 타입을 사용할 수 있다.

const numArr = [1,2,3]
const firstNum = getFirstElem(numArr)

const stringArr = ['1','2','3']
const firstStr = getFirstElem(stringArr)

 

사용

  • javascript 기본 객체

[제네릭 적용 전]

 

[제네릭 적용 후]

 

 

  • React

[제네릭 적용 전]

 

[제네릭 적용 후]

 

추가

Default Value

아래 코드처럼 기본 값을 넣어줄 수 있습니다. 만약 ApiResponse 호출시에 아무 제네릭 타입도 지정하지 않는다면 에러가 나지만 아래와 같이 기본값을 넣어주면 제네릭 타입을 지정하지 않더라도 기본 타입이 status:number타입이 되게 됩니다.

type ApiResponse<Data = {status:number}> = {
  data:Data
  isError:boolean
}

type TempResposne = ApiResponse

[적용 이전]

 

[적용 이후]

 

Extends

extends로 사용 가능한 타입을 제한할 수 있습니다. 아래 코드처럼 extends object로 타입을 제한하면 object 타입이 포함된 타입만 제네릭 지정이 가능하게됩니다.

type ApiResponse<Data extends object> = {
  data:Data
  isError:boolean
}

type TempResposne = ApiResponse<number>

[적용 이전]

 

[적용 이후]