본문 바로가기
FrontEnd/TypeScript

Generics

by y.j 2022. 10. 10.
728x90

Generics이란?

any는 어떤 타입이 들어오는지 상관없이 때문에 타입마다 잘못된 내부 함수를 사용하거나 타입 추론할 때 유용하지 못하다. 하지만 Generic의 경우에는 컴파일 타임에 타입을 추론하여 return과 parameter의 타입을 추론할 수 있도록 도와준다.

function hello(message: any): any {
    return message;
}

console.log(hello("Mark").length);
console.log(hello(39).length);              // undefined가 된다.


function helloGeneric<T>(message: T): T {
    return message;
} 

console.log(helloGeneric('Mark').length);
console.log(helloGeneric(39).length);        // 에러발생

 

정의하는 방법

사용할 때 <>안에 타입을 의미하는 문자를 넣는다.

아래예제에서 T를 사용할 때 특정 타입으로 지정된다면 지정해준 모든 타입이 지정된 타입으로 변하고

지정하지 않더라도 추론된 타입으로 지정된다.

function helloBasic<T>(message: T): T {
    return message;
}

helloBasic<string>("Mark");
helloBasic(36);

 

Array & Tuple

Array

function helloArray<T>(message: T[]): T {
    return message[0]
}

helloArray(['Hello1', 'Hello2']);
helloArray(['Hello1', 39, true]);

Tuple



function helloTuple<T, K>(message: [T, K]): T {
    return message[0];
}

helloTuple(['Hello1', 'Hello2']);
helloTuple([39, 'Hello']);

 

function

type

type HelloFunctionGeneric1 = <T>(message: T) => T; 

const helloFunction1: HelloFunctionGeneric1 = <T>(message: T): T => {
    return message;
};

interface

interface HelloFunctionGeneric2 {
    <T>(message: T): T
};

const helloFunction2: HelloFunctionGeneric2 = <T>(message: T): T => {
    return message;
};

 

class

클래스 이름 옆에 클래스 이름을 표시한다. 클래스 내부에서 유효범위를 가지게 된다.

class Person<T> {
    private _name: T;

    constructor(name: T) {
        this._name = name;
    }
}

extends를 사용하여 T를 제한할 수 있다.

class Person<T extends string | number> {
    private _name: T;

    constructor(name: T) {
        this._name = name;
    }
}

 

keyof & type lookup system

keyof키워드는 데이터 구조체의 필드를 key type으로 변환시킬 수 있다.

interface IPerson {
    name: string;
    age: number;
};

type Keys = keyof IPerson;   // Keys는 "name", "age"를 가지는 타입이 됨

 

getter와 setter를 사용할 때 유용하다.

위 코드를 봤을 때 IPerson의 키가 name이면 string을 age면 number를 가지게 된다. 이것을 단순히 유니온으로 표현한다면 잘못사용될 수 있기 때문에 name일때는 string을 age일때는 number를 쓰도록 강제해야만 한다.

 

변경 전에는 getProp은 문제가 없어보일 수 있지만, return이 될 때, string인지 number인지 확인 할 수 없어 메소드 체인과 같이 return값을 유용하게 사용하지 못한다.

function getProp(obj: IPerson, key: "name" | "age"): string | number {
    return obj[key];
}

 

keyof를 사용해서 K를 "name" | "age"로 강제 한 후 return을 T[K]로 받는다면 return타입이 T[k]의 타입으로 강제된다.

function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

 

setter에서는 obj[key]에 넣을 때 정말 obj[key]에 타입에 맞는 값인지 확인 할 수 없기 때문에 에러를 발생시킨다.


function setProp(obj: IPerson, key: "name" | "age", value: string| number): void {
    obj[key] = value;
}

 

keyof를 사용하여 value의 타입을 확정시킬 수 있다.

function setProp<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
   obj[key] = value;
}
728x90

'FrontEnd > TypeScript' 카테고리의 다른 글

Class static / 싱글톤 / 상속 / Abstract  (0) 2022.10.09
Class 정의 / 초기화 / Getter And Setter  (0) 2022.10.08
Interface  (0) 2022.10.07
TypeScript 컴파일러  (0) 2022.09.28
TypeScript 타입호환성  (0) 2022.09.27

댓글