Typescript의 Generic (3. 중급 사용)

eeensu·2023년 8월 7일
0

typescript

목록 보기
17/22
post-thumbnail

Generic Class

ts에서는 제네릭 클래스(Generic Class)를 통해 클래스 내에서도 제네릭 타입을 사용할 수 있게 지원한다. 제네릭 클래스는 여러 종류의 데이터 유형에 대해 재사용 가능한 클래스를 만드는 데 사용된다.

제네릭 클래스를 사용하면 클래스의 멤버(프로퍼티 및 메서드)에서도 일반적인 타입 대신 제네릭 타입을 사용할 수 있다.

class Box<T> {
    private _value: T;

    constructor(value: T) {
        this._value = value;
    } 

    get value(): T {
        return this._value;
    }

    set value(value: T) {
        this.value = value;
    }
}

const numberBox = new Box<number>(15);			// number 형태의 Box 생성

numberBox.value = 200;							// setter 함수로 인해 가능

const stringBox = new Box<string>("Hello");		// string 형태의 Box 생성

stringBox.value = 12;				// 이미 string으로 할당되었기 때문에 number 값을 넣으면 에러가 발생


또한 class에 generic을 여러개 넣는 것도 가능하다.

class Person<T, U> {
    private _name: T;
    private _age: U;

    constructor(name: T, age: U) {
        this._name = name;
        this._age = age;
    }
}

const p1: Person<string, number> = new Person<string, number>('go', 2134);
const p2: Person<string, number> = new Person('do', 'to');			// 'to' 에 에러 발생




extends

generic class도 extends를 사용할 수 있다. 하지만 일반적인 class의 상속과 거리가 있다.

다음 아래의 예시는 stringnumber를 상속받는 뜻이 아닌, string 혹은 number 만이 T에 할당할 수 있다는 뜻에 가깝다.

ts개발자는 genereic class나 function을 만들 때, 이러한 extends 기능을 활용하여 generic <T>에 어떤 type이 들어올 수 있는지 추측가능하게 만들어야 하는 것이 좋다.

class PersonExtends<T extends string | number> {			// T에는 string or number만 할당 가능
    
    constructor(public _name: T) {		// 다음과 같이 초기화가 가능하다
    }

    public hello(){
        console.log(`hi! ${this._name}!`);
    }
}

const person1 = new PersonExtends<string>('Erick');
const person2 = new PersonExtends(61);
const person3 = new PersonExtends(true);					// 에러




keyof

인터페이스나 타입의 속성 이름들을 추출하는 키워드이다. 이를 통해 객체의 속성 이름을 문자열 리터럴 타입으로 추출하거나, 타입 안에서 특정 속성의 타입을 참조할 수 있다.

keyof를 사용하여 객체의 속성을 추출하면 문자열 리터럴 타입으로 해당 객체의 모든 속성 이름을 나타낸다. 이를 활용하여 유연한 타입 체크나 제네릭 타입을 만들 수 있다.

위와 같이 Person의 keyof 만들어진 PersonKey 타입은 값으로 'name' 또는 'string' 이 올 수 있다. 아래의 예제는 keyof 를 더 다양하게 사용한 예제이다.

interface Student {
    name: string;
    classNumber: number;
}

// U는 T의 prop 값이다.
function getValue<T, U extends keyof T>(obj: T, key: U): T[U] {
    return obj[key];
}

function setValue<T, U extends keyof T>(obj: T, key: U, value: T[U]): void {
    obj[key] = value;
}

// 각각의 get, set함수의 타입을 지정해보면 다음과 같다

// T[U] 는 T 객체의 속성에 들어있는 값이 온다. Gettter에 알맞는 형식이다.
type GetType = <T, U extends keyof T>(obj: T, key: U) => T[U]; 

// Setter는 값만 변경해줄 뿐 아무것도 리턴하지 않으므로 void를 준다.
type SetType = <T, U extends keyof T>(obj: T, key: U, value: T[U]) => void;


개발자는 특정 함수나 클래스를 제작할 때 이러한 Generic을 활용하여 타입을 지정해주는 것이 좋다. 후에 자신이나 다른 팀원이 사용할 때, 잘못된 사용으로 인한 타입 오류를 예방할 수 있고, 코드가 어떤 방향으로 흘러가는지 사전에 예측할 수 있기 때문이다. 따라서 Generic은 ts 코드의 품질을 높이고 유지보수성을 향상시키는 역할을 하는 중요한 기능이라 볼 수 있다.

profile
안녕하세요! 26살 프론트엔드 개발자입니다! (2024/03 ~)

0개의 댓글