Dandy Now!
  • [TypeScript] 제네릭(udemy 강의 "Typescript :기초부터 실전형 프로젝트까지 with React + NodeJS" 섹션 7)
    2024년 03월 14일 00시 04분 24초에 업로드 된 글입니다.
    작성자: DandyNow
    728x90
    반응형

    섹션 7: 제네릭

    📌 제약 조건 작업하기

    제약 조건은 제네릭 타입을 더 효율적으로 최적화된 방식으로 활용할 수 있도록 해 주어서 불필요한 오류나 예기치 않은 작동을 피할 수 있다. 아래 예제에서는 extends object로 제약 조건을 추가한 것이다. Object.assign()의 인수로는 object  타입의 값만 의미가 있다. 만약 제약 조건을 추가하지 않은 상태에서 merge()의 두 번째 인수에 30(number 타입)을 인수로 넣는다면 30은 무시된다. 이런 경우에 타입 스크립트에 의해 걸러지지 않는데, 제약 조건을 추가하면 잘못된 타입의 값임을 알려준다.

    // 제약 조건 ❌
    function merge<T, U>(objA: T, objB: U) {
      return Object.assign(objA, objB);
    }
    
    const mergeObj = merge({ name: "김일남", hobbies: ["Squid Game"] }, 30);
    console.log(mergeObj);
    /*
    출력 결과
    {name: '김일남', hobbies: Array(1)}
    */
    
    // 제약 조건 ⭕
    // 제네릭에서 상속하는 object는 여기에서 소개하는 하나의 예일뿐이다.
    // string, number, union 등 다른 타입이 올 수 있다.
    function merge<T extends object, U extends object>(objA: T, objB: U) {
      return Object.assign(objA, objB);
    }
    
    const mergeObj = merge(
      { name: "김일남", hobbies: ["Squid Game"] },
      { age: 30 }
    );
    console.log(mergeObj);
    /*
    출력 결과
    {name: '김일남', hobbies: Array(1), age: 30}
    */

     

    📌 제약 조건 length

    아래 코드는 length를 제약 조건으로 사용한 케이스로서, 매개 변수의 인수로 문자열이든 배열이든 상관없지만 length 속성은 있어야 한다.

    interface Lengthy {
      length: number; // length의 타입을 지정하지 않으면 아래 코드의 length 메서드에서 타입을 알수 없어 컴파일 에러가 발생한다.
    }
    
    function countAndDescribe<T extends Lengthy>(element: T): [T, string] {
      let descriptionText = "Got no value.";
      if (element.length === 1) {
        descriptionText = "Got 1 element.";
      } else if (element.length > 1) {
        descriptionText = "Got " + element.length + " elements.";
      }
      return [element, descriptionText];
    }
    
    console.log(countAndDescribe("abc")); // ['abc', 'Got 3 elements.']
    console.log(countAndDescribe(["a", "b", "c"])); // [Array(3), 'Got 3 elements.']

     

    📌 제약 조건 keyof

    function extractAndConvert<T extends object, U extends keyof T>(
      obj: T,
      key: U
    ) {
      return "Value: " + obj[key];
    }
    
    console.log(extractAndConvert({ name: "김일남", age: 99 }, "name")); // Value: 김일남
    console.log(extractAndConvert({ name: "김일남", age: 99 }, "age")); // Value: 99

     

    📌 제네릭 클래스

    class DataStorage<T> {
      private data: T[] = [];
    
      addItem(item: T) {
        this.data.push(item);
      }
    
      removeItem(item: T) {
        if (this.data.indexOf(item) === -1) {
          return;
        }
        this.data.splice(this.data.indexOf(item), 1);
      }
    
      getItems() {
        return [...this.data];
      }
    }
    
    const textStorage = new DataStorage<string>();
    textStorage.addItem("김일남");
    textStorage.addItem("김이남");
    textStorage.addItem("김삼남");
    textStorage.removeItem("김이남");
    console.log(textStorage.getItems());
    
    const numberStorage = new DataStorage<number>();
    
    const objStorage = new DataStorage<object>();
    objStorage.addItem({ name: "김일남" });
    const kim2 = { name: "김이남" };
    objStorage.addItem(kim2);
    objStorage.addItem({ name: "김삼남" });
    objStorage.removeItem(kim2); // 객체를 변수에 할당해 사용하지 않으면 메모리의 정확한 주소를 삭제할 수 없다.
    console.log(objStorage.getItems());

     

    📌 유틸리티 타입

    1. Partial

    interface CourseGoal {
      title: string;
      description: string;
      completeUntil: Date;
    }
    
    function createCourseGoal(
      title: string,
      description: string,
      date: Date
    ): CourseGoal {
      let courseGoal: Partial<CourseGoal> = {}; // Partial타입을 통해 속성이 없는 빈객체 사용 가능
      courseGoal.title = title;
      courseGoal.description = description;
      courseGoal.completeUntil = date;
      return courseGoal as CourseGoal; // 반환 타입을 지정해줘야 한다.
    }

     

    2. Readonly

    const names: Readonly<string[]> = ["김일남", "김이남"];
    // names.push("김삼남"); // 읽기 전용이기 때문에 요소 추가시 에러

     

    🤔 제네릭과 유니언 : 전체 함수에서 같은 타입을 사용할 거라면 제네릭 타입이 적합(한 타입에 고정). 메서드를 호출할 때마다 다른 타입을 사용하는 유연성을 갖고 싶다면 유니언 타입이 적합.

    728x90
    반응형
    댓글