TypeScript
TypeScript 제네릭 정의
wlals5855
2024. 9. 19. 22:57
⭐제네릭
✔️제네릭 정의
- 재사용 가능한 컴포넌트(코드단위)를 만드는데 사용
- 해당 컴포넌트가 처리할 데이터타입을 미리 지정하니 않고
해당 컴포넌트를 사용하는 시점에서 원하는 데이터 타입 지정
✔️제네릭 필요성
- 코드의 중복을 줄임
- 재사용 가능한 컴포넌트를 생성
- 타입 안정성 보장
- 컴파일 시점에서 타입을 체크 ('런타임' 환경에서 발생할 수 있는 에러를 방지)
✅컴파일 타임
- 소스코드를 과장
✔️ 제네릭 기본 문법
- 사용할 컴포넌트(변수, 함수, 클래스 등)의 이름 뒤에 꺽쇠괄호(<>) 안에 '타입 변수'를 지정
- 함수나 클래스 등을 사용할 때 활용할 타입 명시
✅"타입 변수"
- 임의의 키워들르 사용하여 명시
- 일반적으로 대문자 알파벳 하나를 사용(T: type, U,...)
- 해당 타입 변수는 컴포넌트 내에서 실재 데이터 타입의 자리를 대신
function generic<T>(arg: T): T { // 함수 정의 시 타입 변수 지정
// 타입 변수 T (단일 타입 변수)
return arg;
}
let str1 = generic<string>('안녕하세요'); // 함수 호출 시 타입 변수에 활용할 타입을 명시
let str2 = generic('hello'); // 오류X - 제네릭의 경우 타입 변수에 값을 유추 가능
// let str3 = generic<string>(100); - Error
generic<number>(123);
generic<boolean>(true);
//? +) 여러 제네릭 타입 변수 지정
// : 여러 개의 독립적인 타입을 처리할 경우 사용
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
let pairOutput = pair<string, number>('안녕', 345);
let pairOutput2 = pair<number, string>(345, '안녕');
function pair2<T, U>(first: T, second: T): U { // U를 T[]로 작성 가능
return [first, second] as U;
}
let pair2Output = pair2<string, string[]>('hello', 'hi');
// ['hello', 'hi']
✔️ 제네릭을 사용하는 컴포넌트들 예
✅ 변수
let a;
a = 10;
a = '안녕';
✅ 제네릭 함수
- 주로 매개변수의 타입을 지정하는 경우가 많음
function genericFunc<T>(args: T[]): T[] { // string[], number[]
console.log(`배열의 길이: ${args.length}`);
return args;
}
let resultFunc = genericFunc<boolean>([true, false, false]); // 배열의 길이: 3
console.log(`함수의 반환값: ${resultFunc}`); // 함수의 반환값: true,false,false
✅제네릭 인터페이스
- 타입 매개변수 T를 가지는 인터페이스
interface IGeneric<T> {
(key: T): T; // 인터페이스 내부의 속성 타입을 명시
}
function example<T>(arg: T): T {
return arg;
}
let myGeneric: IGeneric<number> = example;
console.log(example(5));
console.log(myGeneric(5));
✅ 제네릭 클래스
class GenericClass<T> {
value: T;
add: (x: T, y: T) => T; // 해당 클래스로 인스턴스화될 때 인자로 함수를 전달받음
constructor(value: T, addFunc: (x: T, y: T) => T) {
this.value = value;
this.add = addFunc;
}
}
class BasicClass {
value: string;
constructor(value: string, addFunc: (x: string, y: string) => string) {
this.value = value;
this.add = addFunc;
}
add: (x: string, y: string) => string;
}
let myGenericNumber = new GenericClass<number>(0, function (x, y) { return x + y });
console.log(myGenericNumber.add(4, 5)); // 9
let myGenericString = new GenericClass<string>('', function (x, y) { return `${x}${y}` });
console.log(myGenericString.add('이', '승아')); // 이승아
✅ 제네릭 함수 구현
function reverseArray<T>(array: T[]): T[] {
// 배열 메서드: reverse()
// >> [1, 2, 3].reverse() === [3, 2, 1]
let reverseArr = array.reverse();
return reverseArr;
}
let stringArr = reverseArray(['1', '2', '3']);
console.log(stringArr); // [ '3', '2', '1' ]
let booleanArr = reverseArray([true, false, false, false]);
console.log(booleanArr); // [ false, false, false, true ]
✅ 제네릭 인터페이스 정의
interface KeyValue<K, V> { // K: key의 타입, V: value의 타입
key: K,
value: V
}
let keyValue: KeyValue<string, number> = {
key: '이승아',
value: 123
}
let anotherKeyValue: KeyValue<boolean, number> = {
key: true,
value: 200
}
⭐ 제네릭의 제약 조건
✔️제약 조건(constraints)
타입 매개변수가 특정 조건을만족해야한다는 것을 명시하는 방법
제네릭 타입의 사용 범위를 제한
interface ILength {
length: number;
}
✔️T라는 타입 변수는 ILength 인터페이스를 포함하는 타입이어야 한다.
- '타입 변수' extends(확장) '반드시 포함할 타입'
- T 타입의 데이터는 반드시 length 속성을 가져야 함
function constraints<T extends ILength>(arg: T): T {
console.log(arg.length);
return arg;
}
let data01 = constraints({
length: 10,
value: 3,
addedOption: '안녕하세요'
});
- length 속성을 반드시 포함하는 객체를 인자로 전달
- 추가로 다른 속성 명시 가능 (확장)
console.log(data01); // 10, { length: 10, value: 3, addedOption: '안녕하세요' }
✅ 최소한의 속성을 가진 객체 찾기
keyof 연산자
- 객체 형태의 타입을 따로 속성만 뽑아 유니온 타입으로 만들어주는 연산자
- 모든 키의 이름을 유니온 타입으로 반환
type Type = { // name과 age 속성을 가지는 Type 타입 별칭
name: string;
age: number;
}
type Union = keyof Type;
// Union = name | age;
✔️조건부 타입
- 타입 매개변수에 대한 조건 표현식을 사용하여 타입을 결정하는 방식
- if문과 유사한 기능
type Check<T> = T extends string ? 'String' : 'Not a String';
type Type1 = Check<string>; // String
type Type2 = Check<number>; // Not a String
function checkType<T>(value: T): Check<T> {
let result = typeof value === 'string' ? ('String' as Check<T>) : ('Not a String' as Check<T>);
return result;
}
console.log(checkType('안녕하세요')); // String
console.log(checkType(500)); // Not a String