typescript- 다형성 & generic type

현우.·2024년 9월 26일

typescript

목록 보기
3/5
post-thumbnail

다형성

polymorphism이라고도 불리며 ts에서 다형성이란 여러 타입을 가져 어러 형태를 가짐을 의미한다.

type Print = {
  (arr : number[]) : void;
  (arr : string[]) : void;
}

const arrFunc : Print = (arr) => arr[0]

const result1 =arrFunc([1,2,3]); 
const result2 =arrFunc(['1','2','3']); 

우리는 하나의 함수에 다양한 call signature가 존재하게 해 오버로딩 됨으로서 함수가 다형성을 가짐을 알 수 있다.

의구심😓

그러나 우리는 이런 생각을 할 수 있다.
만약 새로운 call signature가 필요할 경우 계속 추가해서 적용시켜야 할까?
물론 그럴 수 있다.

type Print = {
  (arr : number[]) : void;
  (arr : string[]) : void;
  (arr : (string |number)[]) : void;
  (arr : (string| number |boolean)[]) : void;

}

const arrFunc : Print = (arr) => arr[0]

const result1 =arrFunc([1,2,3]); 
const result2 =arrFunc(['1','2','3']); 
const result3 =arrFunc([1,'2']);
const result4 =arrFunc([1,'2',false]); 

물론 가능은 하지만 굉장한 노가다에 효율적이지 못한 코드들이라고 할 수 있다..
이를 해결하기위해 나온 타입이 generic type 이다.

generic type

제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여,
단일 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법이다.

사용법

  type Print = <T>(arr : T[]) => void
  
  const arrFunc : Print = (arr) => arr[0]

  const result1 =arrFunc([1,2,3]); 
  const result2 =arrFunc(['1','2','3']); 
  const result3 =arrFunc([1,'2']);
  const result4 =arrFunc([1,'2',false]); 

즉 generic type은 타입에 맞는 call signature을 넣을 필요 없이 함수 호출시 필요한 타입을 알아서 정해준다.

꺽새(<>)안에 오는 식별자는 어떤 값을 넣어도 상관이 없으며 나중에 타입이 들어올 자리이다.

any와 generic type의 차이점

any를 쓰면 TS의 보호장치에서 벗어나기 때문에 에러를 잡아내기 힘들다.
따라서 generic을 사용해 코드에 따른 타입을 보장받는 것이 좋다.

any를 사용할 경우

  type Print =  (arr : any[]) => any
  
  const arrFunc : Print = (arr) => arr[0]

  const result1 =arrFunc([1,2,3]); 
  const result2 =arrFunc(['1','2','3']); 
  const result3 =arrFunc([1,'2']);
  const result4 =arrFunc([1,'2',false]); 

console.log(result1.toUpperCase());  // 런타임시 에러발생

result1의 값은 숫자 1이라 toUpperCase() 메서드는 적용될 수 없지만 타입을 any로 지정해놨기 때문에
컴파일시 에러가 발생하지 않는 문제가 발생한다.

generic type을 복수로 설정

generic 타입은 인자의 갯수에 따라 복수로 사용할 수 있다.

type Print = <T,M>(arr : T[], b: M ) => T
 

const arrFunc : Print = (arr) => arr[0]

const a= arrFunc([1,2,3,4],'one');
const b= arrFunc(['1','2','3'],1);
const c= arrFunc([false,true,false,false],true)
const d= arrFunc([1,2,'3',false],{});

generic type은 call signature에서도 쓰일 수 있지만 함수에서도 사용할 수 있다.

일반 함수에서의 사용

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

const a= arrFunc3([1,2,3,4]);
const b= arrFunc3(['1','2','3']);
const c= arrFunc3([false,true,false,false])
const d= arrFunc3([1,2,'3',false]);

화살표 함수에서 사용

const arrFunc2 = <T>(arr: T[]) => arr[0]

const a= arrFunc2([1,2,3,4]);
const b= arrFunc2(['1','2','3']);
const c= arrFunc2([false,true,false,false])
const d= arrFunc2([1,2,'3',false]);

generic을 활용한 코드의 확장성

제네릭 타입을 이용하여 원하는대로 코드 확장 및 타입 생성,
다른 타입 속으로 넣어서도 사용이 가능하다.

type Player<E> ={
  name: string,
  extraInfo :E,
}

type ExtraInfo = {
  favFood :string
}

const hw : Player<ExtraInfo> = {
  name: 'hyunwoo',
  extraInfo: {
    favFood :'gogi'
   	 }
}

const vain : Player<string> = {
  name: 'vain',
  extraInfo : 'ad'
  }

Player 객체 타입의 extraInfo에 대한 타입이 정해지지 않았을때 generic을 사용해 생성시점으로 미룰 수 있다.
이렇게하면 Player 타입을 가지는 변수들이 좀더 유연하게 타입을 선언할 수 있다.

profile
학습 기록.

0개의 댓글