- [TypeScript] 제네릭(udemy 강의 "Typescript :기초부터 실전형 프로젝트까지 with React + NodeJS" 섹션 7)2024년 03월 14일 00시 04분 24초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
섹션 7: 제네릭
📌 제약 조건 작업하기
제약 조건은 제네릭 타입을 더 효율적으로 최적화된 방식으로 활용할 수 있도록 해 주어서 불필요한 오류나 예기치 않은 작동을 피할 수 있다. 아래 예제에서는 extends object로 제약 조건을 추가한 것이다. Object.assign()의 인수로는 object 타입의 값만 의미가 있다. 만약 제약 조건을 추가하지 않은 상태에서 merge()의 두 번째 인수에 30(number 타입)을 인수로 넣는다면 30은 무시된다. 이런 경우에 타입 스크립트에 의해 걸러지지 않는데, 제약 조건을 추가하면 잘못된 타입의 값임을 알려준다.
// 제약 조건 ❌ function merge<T, U>(objA: T, objB: U) { return Object.assign(objA, objB); } const mergeObj = merge({ name: "김일남", hobbies: ["Squid Game"] }, 30); console.log(mergeObj); /* 출력 결과 {name: '김일남', hobbies: Array(1)} */ // 제약 조건 ⭕ // 제네릭에서 상속하는 object는 여기에서 소개하는 하나의 예일뿐이다. // string, number, union 등 다른 타입이 올 수 있다. function merge<T extends object, U extends object>(objA: T, objB: U) { return Object.assign(objA, objB); } const mergeObj = merge( { name: "김일남", hobbies: ["Squid Game"] }, { age: 30 } ); console.log(mergeObj); /* 출력 결과 {name: '김일남', hobbies: Array(1), age: 30} */
📌 제약 조건 length
아래 코드는 length를 제약 조건으로 사용한 케이스로서, 매개 변수의 인수로 문자열이든 배열이든 상관없지만 length 속성은 있어야 한다.
interface Lengthy { length: number; // length의 타입을 지정하지 않으면 아래 코드의 length 메서드에서 타입을 알수 없어 컴파일 에러가 발생한다. } 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("abc")); // ['abc', 'Got 3 elements.'] console.log(countAndDescribe(["a", "b", "c"])); // [Array(3), 'Got 3 elements.']
📌 제약 조건 keyof
function extractAndConvert<T extends object, U extends keyof T>( obj: T, key: U ) { return "Value: " + obj[key]; } console.log(extractAndConvert({ name: "김일남", age: 99 }, "name")); // Value: 김일남 console.log(extractAndConvert({ name: "김일남", age: 99 }, "age")); // Value: 99
📌 제네릭 클래스
class DataStorage<T> { 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); } getItems() { return [...this.data]; } } const textStorage = new DataStorage<string>(); textStorage.addItem("김일남"); textStorage.addItem("김이남"); textStorage.addItem("김삼남"); textStorage.removeItem("김이남"); console.log(textStorage.getItems()); const numberStorage = new DataStorage<number>(); const objStorage = new DataStorage<object>(); objStorage.addItem({ name: "김일남" }); const kim2 = { name: "김이남" }; objStorage.addItem(kim2); objStorage.addItem({ name: "김삼남" }); objStorage.removeItem(kim2); // 객체를 변수에 할당해 사용하지 않으면 메모리의 정확한 주소를 삭제할 수 없다. console.log(objStorage.getItems());
📌 유틸리티 타입
1. Partial
interface CourseGoal { title: string; description: string; completeUntil: Date; } function createCourseGoal( title: string, description: string, date: Date ): CourseGoal { let courseGoal: Partial<CourseGoal> = {}; // Partial타입을 통해 속성이 없는 빈객체 사용 가능 courseGoal.title = title; courseGoal.description = description; courseGoal.completeUntil = date; return courseGoal as CourseGoal; // 반환 타입을 지정해줘야 한다. }
2. Readonly
const names: Readonly<string[]> = ["김일남", "김이남"]; // names.push("김삼남"); // 읽기 전용이기 때문에 요소 추가시 에러
🤔 제네릭과 유니언 : 전체 함수에서 같은 타입을 사용할 거라면 제네릭 타입이 적합(한 타입에 고정). 메서드를 호출할 때마다 다른 타입을 사용하는 유연성을 갖고 싶다면 유니언 타입이 적합.
728x90반응형'언어·프레임워크 > TypeScript' 카테고리의 다른 글
댓글