타입스크립트/타입스크립트 고급

제네릭

webmaster 2022. 10. 11. 12:52
728x90

제네릭 기본 문법

/*
function logText(text) {
    console.log(text)
    return text
}

logText(10) //10
logText('하이') //문자열 '하이'
logText(true)//진위값 true
*/
function logText<T>(text: T): T {
    console.log(text)
    return text
}
logText<string>('하이') //파라미터 타입과 리턴 타입을 실행하는 시점에 넣어준다.
  • 일반적으로 타입을 쓰지 않게 되면, 모든 타입을 받을 수 있는 any타입으로 선언된다.
  • 제네릭 타입을 사용하게 되면, 런타임 시점에 파라미터 타입과 리턴 타입을 받을 수 있다

기존 타입 정의 방식과 제네릭 차이(함수 중복 선언의 단점)

function logText(text:string){
    console.log(text)
    //text.split('').reverse().join('') //text가 string 타입이기 때문에 가능한 것이다
    return text
}

function logNumber(num: number){
    console.log(num)
    return num
}

//모든 타입을 받을 수 있다 -> 암묵적으로 logText에서 타입을 받지 않았기 때문
logText('a')
var num = logNumber(10);//이렇게 하면 문제 없이 동작한다. -> 단 유지보수 관점에서 중복되는 코드를 계속 작성해 나가므로 좋지 않다.
  • 같은 기능을 제공하는 함수에 타입과, 메서드 명을 다르게 하여 제공하면 여러 타입을 파라미터로 받을 수 있다.
  • 단, 이러한 함수 중복은 중복 코드 문제를 야기하며, 유지보수 관점에서도 좋지 않다.

기존 문법과 제네릭 차이(유니온 타입을 이용한 선언 방식의 문제점)

function logText(text:string | number){ //유니온 타입 //함수 하나에서 여러 타입을 받는다
    console.log(text)
    //text는 string, number을 교집합만, 사용할 수 있다.
    return text
}
var a = logText('a'); //a타입은 string | number가 된다
logText(10)
  • 유니온 타입을 사용하면, 함수 중복 문제를 해결할 수 있다.
  • 단, 유니온 타입을 단점이 있다.
    • 유니온 타입으로 온 파라미터는 교집합 API만 제공한다.
    • 결과로 반환된 타입 역시 string | number라는 유니온 타입이 된다.

제네릭의 장점과 타입 추론에서의 이점

function logText<T>(text): T { //입력 받을 타입과 리턴타입을 제네릭으로 받는다.
    console.log(text)
    return text
}
const str = logText<string>('a') //반환되는 타입과 파라미터 모두 string으로 들어간다.
str.split('') //str이라는 값이 string이라는 것을 알 수 있다.
const login = logText<boolean>(true)
  • 입력받을 타입과 리턴 타입을 런타임 시점에 받을 수 있다.
  • 반환되는 타입이 제네릭이므로 해당 타입을 반환받았을 때에는 그에 상응하는 API를 곧잘 쓸 수 있다.
  • 여러 API를 만들어도 중복 없이 만들 수 있다.

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

//인터페이스에 제네릭을 선언하는 방법
/*
interface Dropdown {
    value: string;
    selected: boolean;
}

//const obj: Dropdown = {value: 10, selected: false} //오류
 */
interface Dropdown<T> {
    value: T;
    selected: boolean
}
const objNumber: Dropdown<number> = {value: 10, selected: false} //제네릭 선언
const objString: Dropdown<string> = {value: "abc", selected: false} //제네릭 선언
  • 인터페이스에 제네릭을 선언하면 런타임 시점에 value에 타입을 주입할 수 있다.
  • 여러 타입을 받는 인터페이스를 중복 선언할 필요가 없다

제네릭의 타입 제한


//제네릭의 타입 제한
function logTextLength<T>(text: T[]): T[]{
    console.log(text.length) //text.length가 되지 않는다 why? text에 어떤 값이 들어올지 ts가 알 수 없다.
    text.forEach(function (text){
        console.log(text)
    })
    return text
}

logTextLength(['hi', 'abc']) //
  • 제네릭에 string에 종속적인 API를 호출하게 되면, TS 가 해당 타입이 어떤 것인지 명확하게 알 수 없기 때문에 사용할 수 없다.
  • 이를 해결하기 위해서는 타입 제한을 주어 TS에 힌트를 주어야한다 -> 타입 제한

정의된 타입으로 타입을 제한하기

interface LengthType {
    length: number;
}

function logTextLength<T extends LengthType>(text: T): T { //T라는 것은 LengthType을 하위 타입으로 반드시 length가 있다
    text.length;
    return text
}

logTextLength('a')//a라는 것은 length가 존재하기 때문에 오류가 안난다.
//logTextLength(10) //오류, 숫자 10에는 length가 없기 때문
logTextLength({length: 10})
  • extends라는 키워드를 통해 제네릭타입에도 제한을 걸 수 있다.
    • 현재는 반드시 length라는 프로퍼티(필드)가 존재해야 한다.

keyOf로 제네릭 타입 제한 하기

//제네릭 타입 제한 3 - keyOf
interface ShoppingItem{
    name: string;
    price: number;
    stock: number;
}
function getShoppingItemOption<T extends keyof ShoppingItem>(itemOption: T):T { //타입에 인터페이스에 존재하는 키속성만 받게 하겠다
    return itemOption
}
getShoppingItemOption("name")
  • keyOf를 통해 인터페이스에 존재하는 키값으로 제약할 수가 있다.
728x90

'타입스크립트 > 타입스크립트 고급' 카테고리의 다른 글

클래스  (1) 2022.10.11
이넘  (0) 2022.10.10
연산자를 이용한 타입 정의  (0) 2022.10.10
타입 별칭  (0) 2022.10.10
인터페이스  (0) 2022.10.07