JSTS
[TS] 5. 제네릭
Jaaaay
2022. 7. 7. 17:43
제네릭(Generics)
제네릭은 재사용성이 높은 컴포넌트를 만들 때 자주 활용되는 특징이다. 특히 한가지 타입보다 여러 가지 타입에서 동작하는 컴포넌트를 생성하는데 사용된다.
제네릭은 타입을 마치 함수의 파라미터의 개념으로 사용한다.
제네릭의 기본 문법
function logText<T>(text: T):T{
console.log(text);
return text;
}
logText<string>('하이');
기존 타입 정의 방식과 제네릭의 차이점
- 함수 중복 선언의 단점
function logText(text: string) {
console.log(text);
return text;
}
function logNumber(num: number) {
console.log(num);
return num;
}
logText('a');
logText(10); // 오류
const num = logNumber(10);
logText(true); // 오류
→ 유지 보수 측면에서 좋지 않음
- 유니온 타입을 이용한 선언 방식의 문제점
'string | number' 형식에 'split' 속성이 없습니다.
'number' 형식에 'split' 속성이 없습니다.ts(2339)
→ a에 대해서 다음과 같은 오류가 뜬다. a는 string | number 타입으로 정의되어 있다.
- 제네릭의 장점과 타입 추론에서의 이점
function logText<T>(text: T): T{
console.log(text);
return text;
}
const str = logText<string>("hi");
str.split("");
const login = logText<boolean>(true);
→ 제네릭을 통한 타입 추정이 가능
인터페이스를 이용한 타입 정의 예제
interface Email {
value: string;
selected: boolean;
}
const emails: Email[] = [
{ value: "naver.com", selected: true },
{ value: "gmail.com", selected: false },
{ value: "hanmail.net", selected: false },
];
interface ProductNumber {
value: number;
selected: boolean;
}
const numberOfProducts: ProductNumber[] = [
{ value: 1, selected: true },
{ value: 2, selected: false },
{ value: 3, selected: false },
];
function createDropdownItem(item: Email | ProductNumber) {
const option = document.createElement("option");
option.value = item.value.toString();
option.innerText = item.value.toString();
option.selected = item.selected;
return option;
}
// NOTE: 이메일 드롭 다운 아이템 추가
emails.forEach(function (email) {
const item = createDropdownItem(email);
const selectTag = document.querySelector("#email-dropdown");
selectTag.appendChild(item);
});
numberOfProducts.forEach(function (product) {
const item = createDropdownItem(product);
})
제네릭을 이용한 타입 정의
- 인터페이스에 제네릭을 선언하는 방법
interface Dropdown<T> {
value: T;
selected: boolean;
}
const obj: Dropdown<string> = {value: 'abc', selected: false};
obj.value.split('')
- 제네릭을 이용한 타입 정의 예제
interface DropdownItem<T> {
value: T;
selected: boolean;
}
const emails: DropdownItem<string>[] = [
{ value: "naver.com", selected: true },
{ value: "gmail.com", selected: false },
{ value: "hanmail.net", selected: false },
];
const numberOfProducts: DropdownItem<number>[] = [
{ value: 1, selected: true },
{ value: 2, selected: false },
{ value: 3, selected: false },
];
function createDropdownItem<T>(item: DropdownItem<T>) {
const option = document.createElement("option");
option.value = item.value.toString();
option.innerText = item.value.toString();
option.selected = item.selected;
return option;
}
// NOTE: 이메일 드롭 다운 아이템 추가
emails.forEach(function (email: DropdownItem<string>) {
const item = createDropdownItem<string>(email);
const selectTag = document.querySelector("#email-dropdown");
selectTag.appendChild(item);
});
numberOfProducts.forEach(function (product: DropdownItem<number>) {
const item = createDropdownItem<number>(product);
});
제네릭의 타입 제한
- 제네릭의 타입 제한 1
function logTextLength<T>(text: T[]): T[]{
console.log(text.length);
text.length
text.forEach(function(text){
console.log(text);
})
return text;
}
logTextLength<string>(['hi','abc']);
함수 내에서 length 속성이 있는지 알기 위해서는 배열 요소라는 것을 타입스크립트에 알려준다.
- 제네릭의 타입 제한 2 - 정의된 타입 이용하기
interface LengthType {
length: number;
}
// 정의된 타입을 이용해 제네릭의 타입을 제한 (extends)
function logTextLength<T extends LengthType>(text: T): T {
text.length;
return text;
}
logTextLength("a"); // 기본적으로 문자열에서 length가 제공되기 때문에 ok
logTextLength(10); // length가 제공되지 않으므로 오류
logTextLength({ length: 10 }); // 객체라도 length가 있으므로 ok
logTextLength({ leng: 10 }); // length가 없으므로 오류
- 제네릭의 타입 제한 3 - keyof
interface ShoppingItem {
name: string;
price: number;
stock: number;
}
// T를 타입으로 가지는 변수에 들어갈 값을 제한
function getShoppingItemOption<T extends keyof ShoppingItem>(itemOption: T): T {
return itemOption;
}
// getShoppingItemOption<number>(10);
// getShoppingItemOption<string>('a');
getShoppingItemOption("name");