TS Generic

임동현·2022년 5월 4일
0

generic 사용법

사실 Generic은 java와 같은 다른 언어에서 이미 사용되어 온 특징이다.

Generic은 재사용을 목적으로 함수나 클래스의 선언 시점이 아닌, 사용(호출) 시점에 타입을 선언할 수 있게 됩니다.

제네릭을 사용하게 되면 다음의 장점을 누릴 수 있습니다.

(1) 재사용하므로 타입 정의 코드를 줄여나갈 수 있다.

(2) 재사용을 위해 any 범벅을 만들지 않아도 된다. any는 헬퍼의 도움을 받을 수 없지만 제네릭은 타입을 부여해주므로 헬퍼가 작동하므로 generic을 쓰는 것이 훨씬 좋다.

// generic사용. 재사용 가능하면 타이핑도 들어가 자동 완성 해 줌
function logText<T>(param: T): T {
  return param;
}

// any 사용. 재사용은 할 수 있으나 타이핑이 안 됨
function logText(param: any): any {
  return param;
}

꼭 함수뿐만 아니라 인터페이스, 클래스, 함수, 타입 별칭 등에 사용할 수 있습니다.

<> 안의 문자는 꼭 T가 아니어도 됩니다. Type의 줄임말로 관습적으로 T를 사용할 뿐입니다.

// 함수에서 사용
function identity<T>(arg: T): T {
  return arg;
}

// interface에서 사용
interface IValue<T> {
  value: T;
}

// 클래스에서 사용
class Valuable<T> {
  constructor(public value: T) {}
}

generic에 호출 시점에서의 타입 명시
함수를 정의하는 부분에서는 제네릭을 통해 타입을 선언하지 않았지만 <> 내에 특정 타입을 지정함으로써 타입을 지정할 수 있습니다. 보통 이 방식을 많이 사용하죠. 물론 지정하지 않아도 타입을 추론해서 적절한 제레닉 타입을 찾아냅니다.

function identity<T>(arg: T): T {
return arg;
}
// 타입 지정
console.log(identity<number>(3));
console.log(identity<string>("coding"));

// 타입 추론 해 줌
console.log(identity([1, 3, 5]));

리턴하는 타입이 명시된다는 것이 왜 중요하냐면, return된 값으로 활용할 때 헬퍼가 작동하기 때문입니다.

interface, class에 generic 사용

클래스나 인터페이스도 마찬가지로 제네릭을 활용할 수 있으므로 다음과 같이 작성할 수 있습니다.

interface DropDown<T> {
  value: T;
  selected: boolean;
}

const obj: DropDown<number> = {
  value: 1,
  selected: false,
};
class Human<T, K> {
constructor(private _name: T, public age: K) {}
say(): void {
  console.log(`${this._name} is ${this.age} years old`);
}
}

const me = new Human<string, number>("foo", 100);
console.log(me);

Generic 타입 제한

앞서 아래와 같이 Generic에 extends {toString: Function}을 붙여서 특정 메서드를 가진 타입으로만 가능하도록 제한한 부분이 있습니다.

toString 메서드를 가진 타입만 사용하도록 만들 수 있습니다.

function createDropdownItem<T extends { toString: Function }> ... 중략

비슷하게, length 메서드가 있는 타입만 지정할 수 있도록 generic을 제한하는 방법은 다음과 같습니다.

아래 예시는 length 메서드가 있는 타입으로 제한하였습니다.

function logTextLenghth<T extends { length: number }>(text: T): number {
  return text.length;
}

// 잘 안보이면 interface로 분리해보자
interface LengthType {
  length: number;
}

function logTextLenghth<T extends LengthType>(text: T): number {
  return text.length;
}

추가로, keyof를 사용하여 특정 객체의 키값만을 T로 넣을 수 있게할 수 있습니다.

아래 예시는 itemOption에는 name, price, stock만 올 수 있습니다. keyof에 대해 알고 계시면 이해할 수 있습니다.

interface ShoppingItem {
  name: string;
  price: number;
  stock: number;
}

// ShoppingItem의 키 중에서 한가지가 Generic이 된다.
function getShoppingItemOption<T extends keyof ShoppingItem>(itemOption: T): T {
  return itemOption;
}

console.log(getShoppingItemOption("name"));
console.log(getShoppingItemOption("price"));
console.log(getShoppingItemOption("stock"));

typescript에서 js querySelector를 정의한 부분을 사렾보신다면 keyof를 요긴하게 사용한 것을 확인하실 수 있습니다.

profile
프론트엔드 공부중

0개의 댓글