2023 August TIL

at least 30 minutes per day

8/7: Generics


계절학기 시험 끝나고 너무 헤이해졌다…클린코드 (Java 기반) 읽기 전 Generics에 대해 제대로 공부해야겠다 싶었는데 계속 미뤄져서 월요일인 오늘부터 다시 시작! 사실 타입스크립트로 모든 프로젝트를 하지만 너무 기본기 없이 시작한 탓에 잘 모르고 사용하는 것 같다. 제대로 공부해서 알고 써야지…

Generic이란?

클래스/함수에서 사용할 타입을 그 클래스/함수를 사용할 때 결정하는 프로그래밍 기법

  • 정적 타입 언어들 (Java, C++, TS) 는 클래스/함수 선언 시점에서 IO 타입을 정의 \(\rightarrow\) 특정 타입을 위해 만들어졌기 떄문에 다른 타입을 위해 재사용 불가
  • 하지만 Generics 사용 시 클래스/함수의 범용적인 사용 가능
  • any와 다르게 type 정보가 동적으로 결정됨
  • 이해를 돕기 위한 예시: Stack 자료구조 구현
    class Stack {
      private data: any[] = [];
      contructor() {}
    
      push(item: any): void {
        this.data.push(item);
      }
    
      pop(): any {
        return this.data.pop();
      }
    }
    
    • any 사용 \(\rightarrow\) 자료의 타입이 모두 같지 않으므로 런타임에서 항상 타입 검사 필요
    • 그렇다고 number같은 타입 정의 시 범용성이 떨어짐
    • 상속으로 처리 가능하지만 자료형 하나 추가 할때마다 중복 코드 작성해야 함 (번거로움)
    • 타입 추론 역시 가능하나 명시적인 타입 인수 전달 필요할 때 Generics 사용

문법 <T>

CLASS

  • Generic을 사용하겠따는 의미로 <꺽쇠> 안에 Type으로 사용되는 식별자 집어넣기
    class Stack<T> {
      private data: T[] = [];
    
      constructor() {}
    
      push(item: T): void {
        this.data.push(item);
      }
    
      pop(): T {
        return this.data.pop();
      }
    }
    
  • T: Type의 약자, Type variable (식별자로 사용되는 것 다 가능)
    const numberStack = new Stack<number>();
    const stringStack = new Stack<string>();
    numberStack.push(1);
    stringStack.push('a');
    
    • 컴파일러가 return type 알 수 있음 && 에디터에서 자동완성 도와줌!
    • BUT 컴파일 단계에서 검사하므로 런타임에서 막을 수 없다
      numberStack.push('' as any)
      
      • 컴파일 단계의 타입 체크 우회함, 막기 불가능

FUNCTION

  • 배열의 첫번째 요소 출력하는 함수 :
    function first(arr: any[]): any {
      return arr[0];
    }
    
  • Generic 사용:
    function first<T>(arr: T[]): T {
      return arr[0]
    }
    
    first<number>([1, 2, 3]) // 1
    

    두개 이상의 타입 변수

  • 두가지 변수를 받아 쌍으로 반환하는 함수:
    function toPari<T, U>(a: T, b: U): [ T, U ] {
      return [a, b]
    }
    
    toPair<string, number>('1', 1); // ['1', 1]
    toPair<number, number>(1, 2); // [1, 2]
    
  • T 다음으로는 알파벳 순서대로 사용하면 된다 (i,j 처럼)
    • T, U, V, W…
  • 상속된 타입 변수: 입력받을 변수 타입 제한 가능, 자동완성 가능
    function getFirst<T extends Stack<U>, U>(container: T): U {
      const item = container.pop();
      container.push(item);
      return item;
    }
    
    getFirst<Stack<number>, number>(numberStack); // correct way
    
    getFirst<number, number>(1); 
    // Type 'number' does not satisfy the constraint 'Stack<number>'.
    
  • 참고자료: (all from) HyunSeob Github