const names: Array<string | number> = []; // Array<T> : 제네릭 형식
Array<> : <> 안에는 배열에 들어갈 데이터의 타입을 명시한다.제네릭 타입 : 다른 타입에 연결된 타입으로 다른 타입이 무엇인지 명시한다. 따라서 타입스크립트가 더 안정적으로 지원해 줄 수 있다.
const promise: Promise<string> = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("This is done!");
}, 2000);
}); // 이 프로미스는 새 프로미스 객체를 생성하고, 이 객체는 프로미스 상수에 저장된다. Promise<string>
promise.then((data) => {
data.split(" ");
});
제네릭 타입을 사용하면 타입스크립트에게 더 부가적인 정보를 알려줄 수 있다. 예를 들어 프로미스가 나중에 문자열이나 숫자를 반환할 것이라는 정보를 알려줄 수 있다. → 개발하는데 더 편리
// function merge<T extends {}, U>(objA: T, objB: U): T & U
function merge<T extends {}, U>(objA: T, objB: U) {
return Object.assign(objA, objB);
}
const mergedObj = merge({ name: "Zoe" }, { age: 23 }); // {name: 'Zoe', age: 23}
// const mergedObj: {
// name: string;
//} & {
// age: number;
//}
console.log(mergedObj.age); // 23
mergedObj에 저장된 게 두 입력값의 교차 타입임을 이해할 수 있다. → 이제 미상의 객체를 다루는 것이 아니라 특정한 타입을 다루기 때문이다!function merge<T extends {}, U>(objA: T, objB: U) {
return Object.assign(objA, objB);
}
const mergedObj = merge<{ name: string; hobbies: string[] }, { age: number }>(
{ name: "Zoe", hobbies: ["Sports"] },
{ age: 23 }
);
const mergedObj2 = merge({ name: "Zoe" }, { age: 23 });
console.log(mergedObj.age); // 23
const mergedObj3 = merge({ name: "Zoe" }, 23);
console.log(mergedObj3); // {name: 'Zoe'} => 23을 병합하지 않는다.
Object.assign은 서로 다른 객체만을 병합 가능)!merge의 매개변수는 객체로 들어왔으면 좋겠다! → 제약 조건 설정function merge<T extends {}, U extends {}>(objA: T, objB: U) {
return Object.assign(objA, objB);
}
const mergedObj3 = merge({ name: "Zoe" }, { age: 23 });
console.log(mergedObj3); // {name: 'Zoe', age: 23}
interface Lengthy {
length: number;
}
function countAndDescribe<T extends Lengthy>(element: T): [T, string] {
let descriptionText = "Got no value.";
if (element.length === 1) {
descriptionText = "Got 1 element.";
} else if (element.length > 1) {
descriptionText = "Got " + element.length + " elements.";
}
return [element, descriptionText];
}
console.log(countAndDescribe("Hi there!")); // ['Hi there!', 'Got 9 elements.']
console.log(countAndDescribe(["Sports", "Cooking"])); // [Array(2), 'Got 2 elements.']
console.log(countAndDescribe([])); // [Array(0), 'Got no value.']
length 속성을 넣기 위해 인터페이스 생성 후 extendskeyof 제약 조건function extractAndConvert<T extends object, U extends keyof T>(
obj: T,
key: U
) {
return "Value: " + obj[key];
}
console.log(extractAndConvert({ name: "zoe", age: 23 }, "name")); // Value: zoe
class DataStorage<T> {
private data: T[] = [];
addItem(item: T) {
this.data.push(item);
}
removeItem(item: T) {
this.data.splice(this.data.indexOf(item), 1);
}
getItem() {
return [...this.data];
}
}
const textStorage = new DataStorage<string>();
textStorage.addItem("Max");
textStorage.addItem("Zoe");
textStorage.removeItem("Max");
console.log(textStorage.getItem()); // ['Zoe']
DataStorage에 문자열이나 숫자를 저장하고 싶을 수 있는데, 이에 맞는 제네릭 타입을 설정하면 해당 스토리지에는 타입에 맞는 데이터만 넣을 수 있다. 더 명확 & 유연!const objStorage = new DataStorage<object>();
const maxObj = { name: "Max" };
objStorage.addItem(maxObj);
objStorage.addItem({ name: "Zoe" });
objStorage.removeItem(maxObj);
console.log(objStorage.getItem()); // 0: {name: 'Max'} => 자바스크립트에서 객체는 참조 타입이다.
remove 동작 시 indexOf가 제대로 동작되지 않아 -1을 리턴하게 된다. 따라서 데이터의 마지막 요소가 삭제.class DataStorage<T extends string | number | boolean> {
private data: T[] = [];
addItem(item: T) {
this.data.push(item);
}
removeItem(item: T) {
if (this.data.indexOf(item) === -1) {
return;
}
this.data.splice(this.data.indexOf(item), 1); // 객체인 경우 찾지 못해 -1을 리턴 => 마지막 요소가 제거됨.
}
getItem() {
return [...this.data];
}
}
제네릭 클래스 안에 또 제네릭 함수를 사용하는 등 유연하게 사용할 수 있다!
Partial🔗 유틸리티 타입
interface CourseGoal {
title: string;
description: string;
completeUntil: Date;
}
function createCourseGoal(
title: string,
description: string,
date: Date
): CourseGoal {
let courseGoal: Partial<CourseGoal> = {};
courseGoal.title = title;
courseGoal.description = description;
courseGoal.completeUntil = date;
return courseGoal as CourseGoal;
}
Readonlyconst names: Readonly<string[]> = ["Max", "Sports"];
names.push("Zoe"); // readonly error
names.pop(); // readonly error
class DataStorage {
private data: (string | number | boolean)[] = [];
addItem(item: string | number | boolean) {
this.data.push(item);
}
removeItem(item: string | number | boolean) {
if (this.data.indexOf(item) === -1) {
return;
}
this.data.splice(this.data.indexOf(item), 1);
}
getItem() {
return [...this.data];
}
}
→ 따라서 문자열 배열 혹은 숫자 배열로 구분하고자 한다며 제네릭으로 사용하는 것이 좋다. 더 명확하게 데이터 저장 형식을 구분할 수 있다.