Ted's Codding study
제네릭(Generic) 본문
TypeScript
제네릭(Generic) Ted93 2024. 7. 4. 12:05
1. 제네릭 정의
- 재사용 가능한 컴포넌트(코드 단위)를 만드는데 사용
- 컴포넌트가 처리할 데이터 타입을 미리 지정하지 않고
- 해당 컴포넌트를 사용하는 시점에서 원하는 데이터 타입 지정 가능
2. 제네릭의 필요성
- 코드 중복을 줄임, 재사용 가능한 컴포넌트 생성
- 다양한 타입을 처리하는 함수나 클래스 생성 가능
- 타입 안정성 보장
- '컴파일' 시점에 타입을 체크
- '런타임'에서 발생할 수 있는 에러를 방지
컴파일(Compile)
- 소스코드를 작성하고 편집하는 과정
런타임(Runtime)
- 컴파일 과정을 마친 프로그램이 사용자에 의해 실행되어 동작되어지는 때
3. 제네릭 기본 문법
- 사용할 컴포넌트(변수, 함수, 클래스 등)의 이름뒤에 꺽쇠괄호(<>) 안에 타입 변수를 넣는 것
- 함수나 클래스 등에서 사용할 타입을 명시
- 임의의 키워드를 사용하여 명시, 일반적으로 대문자 알파벳 하나를 사용 (T: Type)
- 해당 타입 변수는 컴포넌트 내에서 실제 데이터 타입의 자리를 대신
function generic<T>(arg: T): T {
// T(Type)는 타입 변수 (단일 타입 변수)
// : 해당 타입 변수는 입력 인수의 타입을 T로 지정
// , 함수의 반환 타입을 T로 지정
return arg;
}
let stringOutput = generic<string>('안녕하세요');
// let stringOutput = generic('안녕하세요'); // <string>은 생략 가능
let stringOutput2 = generic<string>(500); // Error
// 'number' 형식의 인수는 'string' 형식의 매개 변수에 할당될 수 없습니다.
let numberOutput = generic<number>(500);
// let numberOutput = generic<number>(500); // <number>는 생략 가능
let numberOutput2 = generic<number>('반갑습니다'); // Error
// 'string' 형식의 인수는 'number' 형식의 매개 변수에 할당될 수 없습니다.
console.log(stringOutput); // 안녕하세요
console.log(numberOutput); // 500
4. 여러 제네릭 타입 변수 지정
- 여러 개의 독립적인 타입을 처리해야 할 때 여러 타입 변수를 활용
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
let pairOutput = pair('안녕', 5);
// let pairOutput2 = pair<string, number>(5, '안녕'); // - Error
console.log(pairOutput);
- 제네릭 타입을 사용했을때, 처음 사진에는 unknown 타입으로
- 그리고 “” (빈 값)을 넣었을 때는 타입이 string으로
- 숫자 5를 넣었을 때는 number 타입으로 된다
5. 제네릭을 사용하는 컴포넌트들 예시
5-1) 제네릭 함수
function genericFunc<T>(args: T[]): T[] {
console.log('배열의 길이:', args.length);
return args;
}
let resultFunc = genericFunc<boolean>([true, false, false]);
console.log(`함수의 반환값: ${resultFunc}`);
// 배열의 길이: 3
// 함수의 반환값: true,false,false
5-2) 제네릭 인터페이스
- 타입 매개변수<T>를 가지는 인터페이스
interface IGeneric<T> {
(arg: T): T; // 객체의 메서드 인터페이스 명시
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: IGeneric<number> = identity;
console.log(identity(5)); // 5
console.log(identity('안녕')); // 안녕
console.log(myIdentity(5)); // 5
5-3) 제네릭 클래스
- 타입 매개변수<T>를 가지는 클래스
- 다양한 타입의 속성이나 메소드를 지정 가능
class GenericClass<T> {
value: T;
add: (x: T, y: T) => T;
constructor(value: T, addFunction: (x: T, y: T) => T) {
this.value = value;
this.add = addFunction;
}
}
let myGenericNumber = new GenericClass<number>(0, function (x, y) {
return x + y;
});
let myGenericString = new GenericClass<string>('', function (x, y) {
return `${x}, ${y}`;
});
console.log(myGenericNumber.add(5, 3)); // 8
console.log(myGenericString.add('테', '드')); // 테, 드
6. 제네릭 예제
문제 1: 제네릭 함수 작성
- reverseArray 제네릭 함수 작성
function reverseArray<T>(array: T[]): T[] {
let reverseArr = array.reverse();
return reverseArr;
}
let stringArr = reverseArray<string>(['a', 'b', 'c']);
console.log(stringArr); // [ 'c', 'b', 'a' ]
let numberArr = reverseArray<number>([1, 2, 3]);
console.log(numberArr); // [ 3, 2, 1 ]
문제 2: 제네릭 인터페이스 정의
- KeyValue라는 제네릭 인터페이스를 정의
- 인터페이스는 key와 value라는 두 개의 속성을 가지며, 각각의 타입은 제네릭으로 지정
interface KeyValue<K, V> {
key: K;
value: V;
}
let keyValue: KeyValue<string, number> = {
key: '테드',
value: 5,
};
let anotherKeyValue: KeyValue<boolean, number> = {
key: true,
value: 55,
};
문제 3: 제네릭을 사용한 고차함수 구현
- mapArray라는 제네릭 고차함수 작성
- 배열과 배열의 각 요소를 변환하는 콜백함수를 매개변수로 전달
- 변환된 요소로 구성된 새 배열을 반환
function mapArray<T, U>(array: T[], callback: (item: T) => U): U[] {
// T타입의 요소를 가진 배열이 map으로 전체 순회
// 해당 타입에 따라 기능 구현 후 U타입의 배열로 반환
return array.map(callback);
}
let numbers = [1, 2, 3, 4, 5];
let doubledNumbers = mapArray<number, number>(numbers, (x) => x * 2);
console.log(doubledNumbers); // [ 2, 4, 6, 8, 10 ]
let strings = ['a', 'b', 'c'];
let uppercasedStrings = mapArray<string, string>(strings, (x) =>
x.toUpperCase(),
);
console.log(uppercasedStrings); // [ 'A', 'B', 'C' ]
노션으로 보고 싶다면?
https://short-echidna-b16.notion.site/Generic-a38258bc662843948f630ae7e9f099be?pvs=4
제네릭(Generic) | Notion
목차
short-echidna-b16.notion.site