타입스크립트 #7 ( 제네릭 )

taehyung·2023년 11월 29일

TypeScript

목록 보기
7/8

제네릭이란 ?

타입스크립트의 제네릭은 코드의 재사용성, 타입 안전성, 유연성을 향상시키는 기능입니다. 제네릭을 사용하면 다양한 타입을 처리할 수 있는 유연한 함수, 클래스, 인터페이스를 작성할 수 있습니다.

제네릭의 장점

  • 타입 안전성 : 제네릭을 사용하면 컴파일 시점에 타입을 검사하여 런타임 오류를 줄일 수 있습니다.
  • 재사용성 : 하나의 함수나 클래스로 다양한 타입을 처리할 수 있어 코드의 재사용성이 증가합니다.
  • 유연성과 명확성 : 코드를 더 유연하게 작성할 수 있으며, 타입에 대한 정보를 명확하게 전달할 수 있습니다.

제네릭의 단점

  • 복잡성 증가 : 제네릭은 코드를 복잡하게 만들 수 있으며, 읽고 이해하기 어렵게 할 수 있습니다.
  • 오용의 위험 : 잘못 사용될 경우 예상치 못한 버그를 발생시킬 수 있습니다.

사용 시 주의사항

  • 명확한 타입 지정 : 가능한 한 명확하게 타입을 지정하고, 제네릭 타입의 범위를 적절히 제한해야 합니다.
  • 과도한 사용 지양 : 필요 이상으로 복잡한 타입을 정의하지 않도록 주의해야 합니다.

사용 케이스

제네릭은 다음과 같은 경우에 유용합니다.

  • 다양한 타입을 처리하는 함수나 클래스를 작성할 때
  • 타입 안전성이 중요한 상황
  • 유사한 로직을 여러 타입에 적용해야 할 때

기본 형태

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

const getArg<string>('getArg');
const getArg<number>(100);

위 경우 제네릭은 함수 호출부에서 타입을 정의하고, 어떠한 타입도 받을 수 있는 <T> 라는 제네릭 타입을 이용하여 작성되었습니다.

함수에 사용되는 <T> 라는 제네릭 식별자는 어떠한 문자여도 상관이 없지만 암묵적으로 T 문자열을 주로 사용합니다.

중요
타입스크립트의 제네릭은 선언부에서 타입을 지정하는 형태가 아닌, 사용하는 부분에서 타입을 지정하여 타입의 자유도를 높힐 수 있습니다. 다만 과도한 사용은 지양해야겠습니다.

Ps. 타입의 자유도를 높힐 수 있는 문법중에 함수 오버로드, 유니온 타입 지정 도 있습니다. 이들 모두의 공통점은 타입을 확장하는데 그 목적을 두고있고, 과도한 사용은 코드 복잡도와 오류를 발생시킬 수 있다는 리스크입니다.


배열 제네릭

  function getSize<T>(arr: T[]): number {
    return arr.length;
  }
  const arr1 = [true,true,false];
  getSize<boolean>(arr1);

  const arr2 = ["a", "b", "c"];
  getSize<string | number>; //유니온, 교차 사용가능

객체 제네릭


  interface Mobile<T> {
    name: string;
    price: number;
    option: T;
  }
  
  interface IphoneOption {
    color: string;
    ram: number;
  }
  
  const m1: Mobile<string> = {
    name: "iphone12Pro",
    price: 1200,
    option: "red",
  };
  
  const m2: Mobile<IphoneOption> = { //인터페이스를 제네릭으로 타입으로 지정
    name: "iphone12",
    price: 1000,
    option: { color: "grey", ram: 512 },
  };

  const m3: Mobile<{ color: string; ram: number }> = {
    name: "iphone15",
    price: 2000,
    option: { color: "white", ram: 1024 },
  };
  interface User {
    name: string;
    age: number;
  }

  interface Car {
    name: string;
    color: string;
  }

  interface Book {
    price: number;
  }

  const user: User = {
    name: "kth",
    age: 31,
  };
  const car: Car = {
    name: "k5",
    color: "white",
  };

  const book: Book = {
    price: 1000,
  };

  function showName(data: any): string {
    return data.name;
  }

  showName(user);
  showName(car);
  showName(book);

실제 Book 인터페이스에는 name 속성이 없음에도 불구하고 에러를 보여주지 않습니다.

book를 출력해보면 undefined가 나옵니다.

  function showName<T>(data: T): string {
    return data.name; //Error : 'T' 형식에 'name' 속성이 없습니다.
  }

함수 부분을 제네릭을 사용하여 변경해줍니다. 그랬더니 T 타입에 name이 있는지 없는지 추론할 수 없게되었습니다. 당연합니다 T 타입은 따로 정의하지 않았기 때문이고, 또한 data 파라미터로 들어오는 값들에 name 속성이 있을거라고 확신할 수 없기 때문입니다.

  function showName<T extends {name:string}>(data: T): string {
    return data.name;
  }

data 는 T 타입인데, {name:string} 을 확장한 형태의 T 타입이 올것이다. 라고 명시해주었습니다.

  showName(user);
  showName(car);
  showName(book);
// Error : 'Book' 형식의 인수는 '{ name: string; }' 형식의 매개 변수에 할당될 수 없습니다.
// 'name' 속성이 'Book' 형식에 없지만 '{ name: string; }' 형식에서 필수입니다.ts(2345)

이렇게 했더니 Book 에 정상적으로 에러가 발생하였습니다.

이상 포스팅을 마치겠습니다.

profile
Front End

0개의 댓글