Generics(제네릭)
- 제네릭은 재사용성이 높은 컴포넌트를 만들 때 자주 활용된다. 즉, 특정 타입에 고정되지 않고 여러 타입에서 동작할 수 있는 컴포넌트를 생성하는데 사용된다.
- 마치 타입을 함수의 변수처럼 유동적으로 지정하여 사용하는 것을 뜻한다.
- 제네릭은 선언 시점이 아닌, 생성 시점에 타입을 명시하여 다양한 타입의 사용을 가능케 한다.
제네릭을 사용하는 이유
제네릭을 사용하지 않을 경우
const getValue1 = (value: number): number => {
return value;
};
console.log(getValue1(15)); // 15
//
const getValue2 = (value: string): string => {
return value;
};
console.log(getValue2("value")); // value
- 위 함수는
value
라는 인자를 받아 리턴하는 함수이다. - 로직은 똑같지만 단지 타입이 다르다는 이유로 함수를 재생성하거나, 혹은 기존 함수의 타입을 변경해야하는 비효율적인 점이 존재한다.
제네릭 대신 any를 사용하면 안되는 걸까?
const getValue = function (value: any): any {
return value;
};
- any를 사용하면, 특정 타입에 구속되지 않고 어떤 값이든 사용할 수 있다. 그런데도 불구하고 사용하지 않는 이유는 어떤 타입이 들어가고 반환되는지 알 수가 없기 때문이다. 즉, 타입 검사 자체를 하지 않으므로 적절하지 않다.
제네릭을 사용할 경우
함수에서의 제네릭
// function expression
const getValue = function <T>(value: T): T {
return value;
};
//
// arrow function
const getValue = <T>(value: T): T => {
return value;
};
//
console.log(getValue<string>("value")); // value
console.log(getValue("value")); // value
//
console.log(getValue<number>(100)); // 100
console.log(getValue(100)); // 100
- 제네릭을 사용할 경우, 타입을 인자처럼 사용할 수 있다. 즉
<T>
부분이 마치 타입만을 위한 매개변수처럼 작용한다고 생각하면 쉽다. 호출부를 확인해보면 타입 부분에<number>
라고 들어가게 되며, 이때T
의 자리는 모두number
가 된다. - 함수를 호출하는 방법은 함수명 뒤에
<>
안에 타입을 기입하는 방법과, 단순히 인자만 전달하는 방법이 있다. 보통 두 번째 방법을 많이 사용하나 복잡한 로직에서 타입 추정이 힘들시 첫번째 방법을 사용하면 된다.
클래스에서의 제네릭
class Person<T, N> {
constructor(public name: T, protected gender: T, private age: N) {}
getAge(): N {
return this.age;
}
}
const person = new Person("yongmin", "male", 30);
console.log(person); // Person { name: 'yongmin', gender: 'male', age: 30 }
console.log(person.getAge()); // 30
- 제네릭 클래스를 선언할 때, 클래스 이름 오른쪽에
<T>
를 붙이며, 인스턴스를 생성할 때 어떤 들어갈 지 지정하면 된다.
제네릭 형식 제약 조건
function text<T>(text: T): T {
console.log(text.length); // Property 'length' does not exist on type 'T'.
return text;
}
- 현재 선언하는 시점에서는, 어떤 타입이 들어올지 모르는 상태이다. 즉 생성하는 시점에서 어떤 타입이 들어오는지 알 수가 없다. 때문에
length
라는 속성에 접근하고자 할 경우, 타입을 알 수 없으므로 에러가 발생한다.
interface Length {
length: number;
}
function text<T extends Length>(text: T): T {
console.log(text.length);
return text;
}
- 위처럼,
extends
를 통해 제네릭에게 타입 힌트를 주어 해당 속성에 접근을 가능케 할 수 있다.
객체 속성 제약
const obj = {
name: "yongmin",
age: 20,
};
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
console.log(getValue(obj, "name")); // yongmin
- 위처럼, 제네릭을 사용하여 객체에 있는 속성들에게만 접근하도록 지정할 수 있다.
K
라는 타입은,T
라는 타입의 키값으로부터 상속받는다.- 리턴 값은
T
타입의 객체에서K
타입 key의value
이다.
Reference
'TypeScript' 카테고리의 다른 글
TIL | Index signature (0) | 2021.10.24 |
---|---|
TIL | Interface VS Type (0) | 2021.10.24 |
TIL | Interface (0) | 2021.10.23 |
TIL | Type Inference (0) | 2021.10.23 |
TIL | Union, Intersection (0) | 2021.10.23 |