[Typescript] 제네릭 (Generics)


제네릭 (Generics)

제네릭은 재사용성이 높은 컴포넌트를 만들 때 자주 쓰인다.
특히, 한가지 타입보다 여러가지 타입에서 동작하는 컴포넌트를 생성하는데 사용된다.
제네릭은 아래와 같이 쓸 수 있다.
T는 들어갈 타입이다.

function logText<T>(text: T): T {
  return text;
}

// string
let txt:string = logText<string>('hi');
// number
let num:number = logText<number>(10);

제네릭 사용 이유

예를 들어 log를 찍고 리턴 함수가 있다.
아래는 string type을 선언해서 string만 받을 수 있다.

function logString(val: string):string {
    console.log(val);
    return val;
}

const aText = logString('a');
aText.charAt(0);

똑같은 기능을 가진 함수에 number type을 넣을려면 같은 함수를 number 타입으로 따로 선언해야 된다는 불편함이 있다.

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

let bNum = logNumber(10);
bNum.toLocaleString();

그러면 Union type으로 선언해서 둘 다 쓸 수도 있다.

function logText(val: string | number) {
    console.log(val);
    return val;
}

let cText = logText('c');

하지만 함수 후 받은 값을 쓸려면 타입이 아직 string or number라 쓰기 제약이 많다.

![ctext값 string number](/assets/img/typescript/ts-generics1.png “ctext값 string number”)

이럴때 아래처럼 Generics를 이용하면 좋다.
string이면 함수 선언 할 때 string타입을 넣어 사용하고, number면 number타입을 넣어서 사용한다.

// generics선언
function logTextGenerics<T> (val: T): T {
    console.log(val);
    return val;
}

// string타입으로 선언
let stringTxt = logTextGenerics<string>('abcd');
stringTxt.split('');

// number타입으로 선언
let numTxt = logTextGenerics<number>(100);
numTxt.toLocaleString();

제네릭 인터페이스

인터페이스에도 제네릭이 사용가능하다.

// 인터페이스 제너릭 선언
interface DropdownItem<T> {
    value: T;
    selected: boolean;
}

// 인터페이스 string
let emailItem: DropdownItem<string>[] = [
    { value: 'naver.com', selected: true },
    { value: 'gmail.com', selected: false },
    { value: 'hanmail.net', selected: false },
];

// 인터페이스 number
let numberOfProductItem: DropdownItem<number>[] = [
    { value: 1, selected: true },
    { value: 2, selected: false },
    { value: 3, selected: false },
];

function logDropdownItem<T>(item: DropdownItem<T>): DropdownItem<T> {
    console.log(item);
    return item;
}

emailItem.forEach(function (email) {
    let logStr: DropdownItem<string> = logDropdownItem(email); 
});

numberOfProductItem.forEach(function (product) {
    let logNum: DropdownItem<number> = logDropdownItem(product);
});

제네릭 타입 제한

타입 제한 1 - 배열

제네릭으로 들어오는 타입을 배열로 한정 시킬 수 있다.
배열안에 string, number 등등 타입들은 제네릭으로 추후에 선언 할 수 있다.

//  제네릭의 타입 제한 1
function logTextLength1<T>(text: T[]): T[] {
    console.log(text.length);
    text.forEach(function (text) {
        console.log(text);
    });
    return text;
}

logTextLength1<string>(['hi', 'abc']);
logTextLength1<number>([1, 2]);

타입 제한 2 - 정의된 타입 이용 (extends)

interface를 extends를 하여 제네릭으로 들어오는 타입을 한정 시킬 수 있다.
아래의 LengthType 인터페이스에 number타입인 length속성을 넣었다.
함수인자에 제네릭 선언후 extends로 LengthType인터페이스를 상속하게 했다.
그러면 number타입인 length속성을 가진 타입만 들어올 수 있게 제한이 가능하다.

// 제네릭 타입 제한 2 - 정의된 타입 이용하기
interface LengthType {
    length: number;
}
// extends: 기존에 정의된 interface를 확장할 때 쓰임
function logTextLength2<T extends LengthType>(text: T): T {
    text.length;
    return text;
}
logTextLength2('abc'); // string은 length속성을 내장하고있음
// logTextLength2(10); // 타입이 number인 length속성이 없어서 에러발생
logTextLength2([10, 20]); // 배열은 length속성을 내장하고있음
logTextLength2({length: 20, name: 'sd'}) //객체에 직접 length속성을 넣어주면 사용가능

Discussion and feedback