TS)제네릭! 타입을 파라미터로

오늘은 제네릭 타입에 대해 공부해 보겠습니다.

함수나 클래스를 만들때, 타입을 지정하지않고 실제로 함수를 호출할때나 클래스의 인스턴스를 만들때 타입을 매개로 전달하는 방법입니다.

기본 코드)

function foo<T>(x: T[]) :T {
  return x[0];
}

let a = foo<number>([4,2])
let b = foo<string>(['kim', 'park'])

// 타입을 알아서 유추해주기 때문에, 아래와 같이 써도 에러가 발생하지 않습니다.
let a = foo([4,2]) // T는 number로 유추한다.
let b = foo(['kim', 'park']) // T는 string으로 유추한다.

T라는 임의의 타입을 지정해두고 함수를 호출할때 number, string 라는 타입을 지정해주고 있습니다.


예시 1)

function bar<T>(x:T){
  return x-1
}

bar<number>(1)  // 에러

위 코드는 에러가 발생합니다. 함수를 정의 할때, T는 어떤 타입이 될 지 정해지지 않았는데 x-1 연산을 수행하기 때문입니다.
이는 narrowing을 통해 해결할 수 있지만, T 자리에 들어오는 타입을 미리 number로 제한해두면 더 간단할 것 같습니다.

function bar<T extends number>(x:T){
  return x-1
}

bar<number>(1) 

extends는 인터페이스 에서 속성값을 '확장,복사' 할 때 사용하였습니다.
제네릭에서도 마찬가지인데, 내장되어있는 number 타입의 속성을 T타입에 그대로 복사하는 것입니다.
T는 number의 속성을 가지고 있으므로, x-1과 같은 연산을 해도 에러가 나지 않습니다.


제네릭 constraint 연습하기

function bar<T>(x:T){
  return x.length;
}

bar<string>('몇글자일까요'); // 에러가 발생합니다.

코드의 목적은, string의 길이를 반환하는 것 입니다.
그러나 x가 어떤 타입이 될 지 정해지지 않으므로, x.length를 하면 에러가 발생하죠
number타입일수도 있으니까요.
x에 length라는 프로퍼티가 무조건 존재하도록 제한을 걸어두어야 할 것 같습니다.

type MyType = { 
  length:number  
}

function bar<T extends MyType>(x:T){
  return x.length;
}
bar<string>('몇글자일까요'); // 성공!

T 타입은 length라는 프로퍼티를 가진다 라고 보장을 해주니, 에러가 해결됐습니다!


Class에 제네릭 적용하기

class Person<T> {
  name;

  constructor(name:T){
    this.name = name;
  }
}

new Person<string>('이름');
new Person<string[]>(['aa','bb','cc'])
new Person<number>(1)

클래스도 마찬가지로 제네릭을 이용하면 확장성에서 큰 장점이 있습니다.

또 연습할만한 제네릭코드가 있다면 추가하겠습니다!

좋은 웹페이지 즐겨찾기