본문 바로가기
TIL

타입스크립트 제네릭

by 은지:) 2023. 7. 23.
728x90
반응형

 

제네릭 <T>

 

 

 

 

 

이거 왜 있지? 했던 문법

 

 

useState 이외에는 써 본 적이 없다

 

 

 

ㅎㅎ

 

 

알아보니

 

함수에 따라 호출시 타입을 정할 수 있다고 한다

그래서 리액트 라이브러리 타입 찾으러 들어갈 때마다 엄청 많이 보였던 거 같음

 

 

멋지게 말하면

 

함수에 타입이 고정되는 걸 방지하고

재사용할 수 있다

 

뭔가 데이터 convert 함수에 잘 쓸 수 있을 거 같음

 

 

 

그래서 작성한 예시

 

 

 

1.

function test<T>(a:T,b:T){
  return a + b;
}

test<string>("1","2")
test<number>(1,2)

⇒ 잉 안 됨

⇒ 리턴문에서 제네릭은 + 모른다고 에러 뜸

⇒ + 연산자가 제네릭 타입에서는 지원 안 됨

⇒ + 연산자는 숫자/문자열을 대상으로 동작하기 때문에 타입스크립트에서도 해당 타입에서만 지원

 

 

 

2. 그럼  t에 타입 정해주자

function test<T extends number | string>(a: T, b: T): T {
  return a + b;
}

test<number>(1, 2); 
test<string>("1", "2");

⇒ 안 됨

⇒ T가 string 이나 number(유니온 타입 설정) 이고 인자와 반환값에 적용

⇒ t가 number | string 하더라도 타입스크립트가 리턴 t의 타입을 런타임 시점에서 확인할 수 없음

 

 

 

3. 그럼 내부에서 타입 분기

function test<T extends number | string>(a: T, b: T): T {
  if (typeof a === "number" && typeof b === "number") {
    return a + b; 
  } else if (typeof a === "string" && typeof b === "string") {
    return a + b; 
  } else {
    throw new Error("Unsupported types"); // 그 외의 타입은 오류 처리
  }
}

const result1 = test<number>(1, 2);
console.log(result1);

const result2 = test<string>("1", "2");
console.log(result2); 

⇒ 안 됨

⇒ + 연산자를 사용하는 제네릭 함수에서는 t에 대해 연산이 가능한 타입을 보장해주어야함

⇒ t에 number string 을 동시에 상속하는 제약 조건을 사용하면 두 타입을 + 연산자로 연결할 수 없음

 

 

 

4. 그럼 함수 미리 만들어놓고 리턴값 타입 정해준 다음에 오버로드

function test(a: number, b: number): number;
function test(a: string, b: string): string;

function test<T extends number | string>(a: T, b: T): T {
  if (typeof a === "number" && typeof b === "number") {
    return a + b;
  } else if (typeof a === "string" && typeof b === "string") {
    return a + b;
  } else {
    throw new Error("Unsupported types");
  }
}

const result1 = test<number>(1, 2);
console.log(result1);

const result2 = test<string>("1", "2");
console.log(result2); 

 

⇒ 안 됨

리턴값 t 는 여전히 string | number

⇒ 제네릭은 타입에 대한 추론과 제약을 제공하지만

⇒ 일반적인 연산자는 타입 추론을 포함한 복잡한 연산들을 수행하기 어려움

 

 

 

 

결론

function test(a: number, b: number): number {
  return a + b;
}

function testString(a: string, b: string): string {
  return a + b;
}

const result1 = test(1, 2);
console.log(result1); 

const result2 = testString("1", "2");
console.log(result2);

안 쓰는게 나음

 

혹은 모두 유니온 처리하고 안에서 type 분기

 

 

 

 

++++++ 이렇게 쓰면 가능

 

 

function test<T extends number | string>(a: T, b: T): T {
  if (typeof a === "number" && typeof b === "number") {
    return (a as any) + (b as any) as T; 
  } else if (typeof a === "string" && typeof b === "string") {
    return a + b as T; 
  } else {
    throw new Error("지원되지 않는 타입입니다.");
  }
}

const result1 = test<number>(1, 2);

 

리턴값이 T 처리 되어서 생기는 문제라면 리턴값에도 타입 설정해주면 됨

에러 던지는 else 처리 안 해줄 시 또 반환값 t에서 오류 뱉음

 

 

좋은 코드는 밑 코드

 

function concat<T> (a:T,b:T):T;

function concat(a:any,b:any){
  console.log(typeof a)
  console.log(typeof b)
  return a + b;
}

concat<string>("Adm","Asd")
concat<number>(11,222)

 

 

 

any? 의미가 없는 거 아닌가? 싶어서 type 찍어봤는데

 

오오오오 잘 돌아간다

 

 

===================

 

 

+ 보통 이렇게 쓰는 듯

 

function arrayConcat<T> (a:T[],b:T[]) {
  return a.concat(b)
}

let array1 = [1,2,3]
let array2 = [2,3,4]

let result = arrayConcat<number>(array1,array2)

 

 

-----------------

 

그 외의 장점으로는

⇒ 타입을 런타임시에 결정하는 것이 아닌 컴파일 단계에 타임을 체킹한다는 것

⇒ 캐스팅 관련 코드 제거 가능

 

 

근데 컴파일 타입 체킹은 제네릭이 아니라 원래 타입스크립트의 장점이 아닌가? 싶었음

 

 

물어보니 chat GPT도 맞다고 한다

 

그래도 장점으로 하나를 꼽아서 다들 이야기하는데

써놓은 이유가 있지 않을까 싶고,,,

 

 

728x90
반응형

'TIL' 카테고리의 다른 글

캐스팅  (0) 2023.07.23
타입 표명 vs 타입 단언  (0) 2023.07.23
타입스크립트 class / implements  (0) 2023.07.09
클로저  (0) 2023.06.21
기명함수 / 익명함수  (0) 2023.06.21

댓글