제네릭을 이용하면 class, function, interface를 다양한 타입으로 재사용할 수 있다.
선언할 때 파라미터만 적어두고 사용할 때 타입을 지정해 주는 방법이다.
기본적인 제네릭 사용 방법
function getSize(arr : number[]) :number {
return arr.length
}
const arr1 = [1,2,3]
getSize(arr1) // 3
const arr2 = ["1","2","3"]
getSize(arr2) // 3
동일한 형태로 만들었지만 매개변수가 number이냐, string이냐 에 따라서 에러가 생기는 부분이 있을 것이다.
이걸 빠르게 해결하기 위해선
유니온을 이용한 방법
function getSize (arr: number[] | string []) : number{
return arr.length
}
이렇게 arr의 타입을 여러 개 지정한다면 가능하다.. (유니온 타입으로 하는 방법 말고 오버로드해서 사용할 수 있다.)
하지만 arr가 확실하게 정해진 것이 아니고 boolean 타입이나 object 타입으로 들어올 경우도 있다.
const arr3 = [true, true, false]
const arr4 = [{}, {}, {name : "baekspace"}]
이렇게 여러 개의 타입이 들어온다면 모든 것을 다 지정해 주는 것보다 제네릭을 이용해서 작성하는 것이 효율적이다.
제네릭을 이용한 방법
위에서 썼던 getSize()를 제네릭을 이용하여 작성하면 아래와 같다.
function getSize<T>(arr:T[]):number{ // 가능
return arr.length
}
function getSize<Key>(arr:Key[]):number{ // 가능
return arr.length
}
const arr1 = [1,2,3]
getSize<number>(arr1) // 가능
getSize(arr1) // 가능
여기서 T는 반드시 T라고 적을 필요는 없다. 많은 곳에서 이렇게 쓰고 있기 때문에 T를 적었지만, A라고 해도 되고 Key라고 해도 가능하다.
사용할 때는 <>를 이용해서 타입을 지정해 주면 된다. (안 쓰더라도 타입스크립트가 유추는 한다.)
interface에서 제네릭 사용 방법
interface Mobile<T> {
name: string
price: number
option: T
}
// object의 타입이 지정되어있다면 그대로 쓸 수 있음.
// <object> -> <{color:string; coupon:boolean}>
const m1 :Mobile<object> = {
name:"s22",
price: 1000,
option :{
color:"red",
coupon:false
}
}
const m2 :Mobile<string> = {
name:"s21",
price: 900,
option :"good"
}
제네릭을 이용해서 하나의 타입만 받지만 여러 형태의 객체를 만들 수 있다.
제네릭 응용
interface User{
name:string
age:number
}
interface Car{
name:string
color:string
}
interface Book{
price:number
}
const user :User = {name:"a", age:10}
const car:Car = {name:"bmw", color:"red"}
const book:Book = {price :3000}
function showName (data) : string{
return data.name
}
showName(user)
showName(car)
showName(book)
이렇게 아무것도 선언하지 않으면 data가 any 타입으로 메서드를 호출하는 곳에서는 문제가 없지만 book를 인자값으로 넣어주는 마지막 메서드를 사용할 때 문제가 생길 수 있다.
그래서 data에 제네릭으로 타입을 지정한다.
function showName<T>(data:T) : string{
return data.name
}
이렇게 작성하고 나면return data.name
에 오류가 발생한다.
Property 'name' does not exist on type 'T'.(2339)
인자값으로 넣어주는 book에는 name이 없기 때문이다. 그래서 name이 있는 인자값만 호출할 수 있도록 T를 더 명확하게 쓰면 된다.
function showName<T extends {name:string}>(data:T) : string{
return data.name
}
이렇게 타입을 지정하게 되면
어떠한 타입이 들어올 때 그 타입은 name 객체를 확장한 형태이다.
라는 것을 알려줄 수 있다.
참고로 name이 string 이여야 하고, name이 없는 book의 경우에는 인자 값으로 사용할 수 없기 때문에
showName(book)
은 사용할 수 없다.
interface Car{
name:boolean
color:string
}
...
showName(car) // 사용 불가
위에서 {name:string} 객체를 확장한 타입이 들어올 것이라고 했지만 Car의 name 타입을 바꾸게 된다면 이것 또한 name이 string이 아니기 때문에 에러를 발생하고, showName(car)
또한 사용할 수 없다.
'🟨 JavaScript 🟨 > 🟦TypeScript🟦' 카테고리의 다른 글
TypeScript - Utility Types (0) | 2024.02.23 |
---|---|
TypeScript - class (feat.예시코드) (1) | 2024.02.22 |
TypeScript - function (0) | 2024.02.22 |
TypeScript - interface (0) | 2024.02.22 |
Jest를 이용한 TDD(Test-Driven Development) (0) | 2023.04.24 |
댓글