Typescript_ 11. Generic_제약조건 작업(T extends something), 클래스 제네릭

Eunsu·2022년 3월 17일
1

@ TypeScript

목록 보기
14/14
post-thumbnail

◼ 제네릭 함수 & 클래스

🚀 function 타입 제약조건

전에 있는 포스팅 코드를 가져와보자.

function mergeData<T, U>(a: T, b: U) {
  return Object.assign(a, b);
}
const user = mergeData({ name: "Jenny", hobbies: ["sing a song", "play game"] }, 27);

만약 두번째 파라미터가 오브젝트가 아닌 number여도 리턴값인 Objext.assign의 계산결과에 따라 우리가 기대하는 값이 안 나왔을 뿐이지, 어떠한 애러도 내지 않는다.

string이 들어갈 경우 글자수를 쪼개서 반환하고, 배열이 들어갈 경우 index값을 key로 걸어두어 새로운 오브젝트가 생성된다.

✔ extends의 사용

function mergeData<T extends object, U extends object>(a: T, b: U) {
  return Object.assign(a, b);
}
const user = mergeData({ name: "Jenny", hobbies: ["sing a song", "play game"] }, 24);

변수 타입에도 extends를 사용해 들어오는 타입을 명확시 시킬 수 있다.

다른 코드의 예를 보자.

function countAndPrint<T>(element: T) {
  let desc: string;
  if (element.length === 1) {
    desc = "one length";
  } else if (element.length > 1) {
    desc = "several length";
  }
  return [element, desc];
}

이런 애러를 반환한다.

  1. 타입스크립트는 element가 어떤 타입으로 들어오는지 정확히 모르기 때문에 length프로퍼티가 없다는 애러
  2. 변수가 할당되기 전에 리턴으로 반환되었다는 애러

이렇게 바꿔보자.

interface Lengthy{
  length:number
}
 //애러 1 해결
function countAndPrint<T extends Lengthy>(element: T) {
  let desc: string = ""; // 애러 2해결
  if (element.length === 1) {
    desc = "one length";
  } else if (element.length > 1) {
    desc = "several length";
  }
  return [element, desc];
}

T extends Lengthy라는 제약조건을 걸어, 이로써 얻는 것이 무엇이든 length속성도 반환되며 배열이나 문자열을 length속성을 지닌다는 것을 알수 있다.

더 구체적으로 정의하기 위해 return값도 튜플 타입으로 타입을 지정해준다.

  function countAndPrint<T extends Lengthy>(element: T):[T,string] {
  let desc: string = "";
  if (element.length === 1) {
    desc = "one length";
  } else if (element.length > 1) {
    desc = "several length";
  }
  return [element, desc];
}

🚀 제네릭 클래스

class storage를 만들어 보자.

  class StorageItems {
  private data = [];
  addItem(item) {
    this.data.push(item);
  }
  removeItem(item) {
    this.data.splice(this.data.indexOf(item), 1);
  }
  getItems() {
    return [...this.data];
  }
}

이렇게 코드를 작성하면, item에 대한 타입이 없으므로 애러를 발생한다.

이때 제네릭을 사용해주면 된다.

  class StorageItems<T> {
  private data:T[] = [];
  // T 타입의 배열을 입력함으로써 제네릭 타입의 데이터가 저장되도록 한다.
  addItem(item: T) {
    this.data.push(item);
  }
  removeItem(item : T) {
    this.data.splice(this.data.indexOf(item), 1);
  }
  getItems() {
    return [...this.data];
  }
}

생성자를 만들어 보자

  const textStorage = new StorageItems();
  textStorage.addItem('hello')

이렇게 아무런 타입을 지정해주지 않으면 StorageItemsunknown타입이 지정된다.

◾ unknown 타입

  • unknown 타입은 어떤 타입의 값도 할당할 수 있다. Top Type이며, 컴파일러에게 어떤 타입이 들어올 지 모르니 추론해달라고 말하는 타입이다. any 타입과 다른점은 unknown 타입을 지정하면 값을 할당 할 수 없다.

생성자의 타입을 string으로 지정한다면

const textStorage = new StorageItems<string>();
textStorage.addItem("hello");
const result = textStorage.getItems();
result[0].toFixed() // error

이렇게 사용 할 수 있음.
만약 <T> 의 타입범위를 어느 타입으로만 한정하고 싶다면, extends를 사용해서 타입의 범위를 한정 할 수 있다.

class StorageItems<T extends string|number|boolean> {
  private data: T[] = [];
  addItem(item: T) {
    this.data.push(item);
  }
  removeItem(item: T) {
    this.data.splice(this.data.indexOf(item), 1);
  }
  getItems() {
    return [...this.data];
  }
}}
}
profile
function = (Develope) => 'Hello World'

0개의 댓글