제네릭(Generics)

14478 단어 typescripttypescript

제네릭이란?

타입이 마치 함수의 매개변수 값으로 설정되는 개념으로, 주로 재사용성이 좋은 컴포넌트를 만들 때 자주 사용된다.

//logText 옆에 <T>은 제네릭을 쓸 것이라고 명시하는 붑분
//text 옆에 T는 함수호출 시 받은 매개변수의 타입으로 설정하는 부분
//{} 앞에 T는 리턴값도 타입을 매개변수의 타입으로 설정하는 부분
function logText<T>(text: T):T{
  console.log(text);
  return text
}
logText<string>("하이") 
//호출 시 <string>을 사용하면 함수의 매개변수와 리턴 값의 타입이 string으로 설정되는 것이고,
//없다면 타입이 "하이"가 된다.
//이 때 하이를 타입추론을 통해 적당한 타입으로 설정된다.

제네릭을 사용하면 함수가 어떤 타입을 받을지 미리 정하지 않고, 넘겨주는 인자를 통해서 함수가 받는 타입과 리턴타입을 설정하게 된다.

이전 문법과 제네릭의 차이점

  • 기존 문법
function logText(text:string):string{
  console.log(text)
  return text;
}
function logNumber(text:number):number{
  console.log(text)
  return text;
}

기존의 문법의 경우 인수의 매개변수의 타입이 다를 때마다 해당하는 함수를 선언해주어야 하기 때문에 불필요한 코드를 반복하게 된다.

  • 유니온 문법
function logText(text : string|number): string|number{
  console.log(text);
  //text. << 이때가 문제
  return text;
}

유니온 문법을 사용할 경우 인풋에 대한 문제는 해결되지만, 함수안에서 text에 대한 api를 사용하고자 할 때와 함수의 리턴값을 받은 변수가 어떤 api를 사용하고자 할 때 string과 number에서 공통적으로 사용되는 api만을 사용할 수 있다.

  • 제네릭 문법
function logText<T>(text : T): T{
  console.log(text);
  return text;
}

const str = logText<string>('a') // 이 때 함수의 매개변수와 리턴 타입은 string
const num = logText(10) //이 때 함수의 매개변수와 리턴 타입은 number

하나의 함수를 정의하여 여러가지의 타입에 대해서 적용가능하며, 여러가지 타입에 대한 적절한 api를 사용할 수 있다.

인터페이스에 제네릭 선언방법

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

const obj: Dropdown<string> = { value : "abc", selected: true};
// value값이 string이 아니면 에러

인터페이스에 타입을 T로 준 부분에 타입은 호출 시에 <타입>에서 준 타입으로 설정된다.

제네릭 타입제한

제네릭을 엄격하게 사용하고 싶을 경우 사용

  • 타입제한
function logTextLength<T>(text : T) : T{
  text.length // error
  return text;
}
logTextLength('hi');

logTextLength 함수 입장에서는 매개변수로 어떤 값이 들어올지 모르기 때문에 length를 사용할 경우 에러를 표시하게 된다.

function logTextLength<T[]>(text : T[]) : T[]{
  text.length // length api를 가지고 있는 []을 타입으로 선언했기 때문에 성공
  return text;
}
logTextLength<string[]>(['hi']);

따라서 위와 같이 []형태로 만들어주면 기본적으로 logTextLength함수는 매개변수로 배열을 받기 때문에 length를 사용할 수있다. 물론 인수값으로는 배열값으로 넣어줘야하는 문제가 있다. 우리는 인수로 string값을 넣고 length를 사용하고 싶기 때문에extends를 사용한다.

  • extends
interface LengthType{
  length: number;
}

function logTextLength<T extends LengthType>(text : T):number{
  const len = text.length;
  return len;
}
logTextLength<string>('a') // 1
logTextLength<string[]>('a') // 1

extends는 특정 속성을 가진 타입만 허용하는 키워드이다. 즉, 원래 T라는 타입에는 length가 없지만 extends를 사용하여 인수 중에 length라는 메소드를 사용할 수 있는 타입은 허용한다.

  • keyof
interface User{
  name: string;
  email: string;
  password: string;
  age: number
}

function logName<T extends keyof User>(value :T):void{
  console.log(value)
}

logName('name');

특정 객체의 키 값을 인수로 허용할 때 keyof 키워드를 사용한다. 키값을 키워드로 사용하는 것 뿐이고 반환 값은 의미를 두지 않아도 된다.

좋은 웹페이지 즐겨찾기