본문 바로가기
🟨 JavaScript 🟨/🟦TypeScript🟦

TypeScript - Generic

by 백씨네 2024. 2. 22.

제네릭을 이용하면 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

댓글