TypeScript

TypeScript 제네릭

honey.kikiki 2021. 10. 16. 16:31
728x90

타입스크립트 제네릭

클래스나 함수의 목적중 하나는 '재사용성'에 있습니다. 이 제네릭을 이용하게되면 재사용성이 한층 더 증가합니다. 일종의 '틀' 이라고 생각하시면 됩니다. 타입스크립트에서 제네릭을 어떻게 쓰는지에대해 알아보도록 하겠습니다.

 

클래스에서의 제네릭

class NumberButton {}
class TextButton {}
class OprButton {}
class ButtonContainer<T> {
    private container: T[];
    getButton(index: number) {
        return this.container[index];
    }
    // ...
}
const numberContainer = new ButtonContainer<NumberButton>();
const textContainer = new ButtonContainer<TextButton>();
const oprContainer = new ButtonContainer<OprButton>();

제너릭의 중요한 부분이 <T> 입니다. type 의 약자로 보통 T 를 쓰지만 변경할 수 있습니다. type 에서 따왔듯이 타입에따라 틀이 바뀐다는 점입니다.

생성할때도 마찬가지로 () 전에 <> 꺾쇠괄호 안에 타입을 주입합니다.

 

함수에서의 제네릭

function getFirst(array: any[]): any {
    return array[0];
}

다음코드는 배열의 첫번째 요소를 반환하는 코드입니다. any를 사용한것을 볼 수 있는데 이것을 제네릭으로 바꿔보겠습니다.

function getFirst<T>(array: T[]): T {
    return array[0];
}

함수의 이름 뒤에 <T> 가 들어간것을 확인할 수 있습니다. 그리고 호출할 때도 타입을 정해줍니다.

getFirst<number>([1, 2, 3]);
getFirst<string>(['h','e','e']);

제약조건

제네릭을 이용하였지만 해당 프로퍼티가 있는지 없는지에 대해 확신할 수 없습니다. 그래서 컴파일러는 다음 코드에 대해 에러를 뱉습니다.

function getHeight<T> (person: T) {
    return person.height; // Error!
    // TS2339: Property 'height' does not exist on type 'T'.
}

이 코드는 개발자 입장에서는 person 이 들어옴을 확신하고 작성했지만 컴파일러에서는 .height라는 프로퍼티가 있는지 없는지 알 수 없기 때문에 생긴 에러입니다. 때문에 컴파일러에게 확신을 주게끔 인터페이스를 생성해주고 extends 키워드로 제약조건을 만들어줍니다.

interface Heightwise {
    height: number;
}
function getHeight<T extends Heightwise> (person: T) {
    return person.height;
}
const myInfo = {
    name: 'kikiki',
    height: 171,
};

console.log(getHeight(myInfo)); // 171