일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- HTML #CSS
- 컴퓨터공학 #c #c언어 #문자열입력
- BOJ #컴퓨터공학 #C++ #알고리즘 #자료구조
- 잔
- 컴퓨터공학 #자료구조 #스택 #c++ #알고리즘 #백준문제풀이
- 컴퓨터공학 #Java #자바 #클래스 #객체 #인스턴스
- Today
- Total
영벨롭 개발 일지
[TypeScript] 타입스크립트의 Generics 본문
📌 타입스크립트 제네릭(Generics)
변수, 함수의 매개변수 등을 정의할 때, number, string 과 같이 항상 타입을 고정하여 사용하였습니다. 하지만 이는 프로그래밍을 함에 따라 유연성과 재사용성이 떨어지게 되는 단점이 있습니다. 물론, any 나 union 타입을 통해 좀 더 유연하게 타입을 사용할 수 있지만, 이는 오류를 쉽게 찾지 못하며 가독성 또한 떨어지게 됩니다.
따라서 타입을 고정하여 명시하지 않고, 언제든 변할 수 있는 유연한 타입으로 표현할 수 있는 장치가 필요합니다. 이를 우리는 제네릭(generic)이라고 부릅니다.
즉, 제네릭(generic)이란 타입을 변수화 한 것이라고 할 수 있습니다. 개발자는 제네릭을 통해 코드의 유연성과 재사용성을 높일 수 있습니다.
✅ 제네릭을 사용하는 이유
- 타입이 고정되는 것을 방지하고, 재사용 가능한 요소를 선언할 수 있다.
- 타입 검사를 컴파일 시간에 진행함으로써 타입 안정성을 보장한다.
- 캐스팅 관련 코드를 제거할 수 있다.
- 코드의 재사용성을 높일 수 있다.
- 오류를 쉽게 포착할 수 있다.
- any 타입을 사용하게 되면, 컴파일 시에 컴파일러가 오류를 찾지 못한다.
📌 Generics - 함수
우선 union 을 사용한 함수를 살펴보겠습니다. 다음과 같이 코드를 작성하게 되면, union 에 타입을 남발하게 되여 코드의 가독성이 떨어지게 됩니다.
function getArrayLength(arr: number[] | string[] | boolean[]): number {
return arr.length;
}
이를 제네릭을 이용하여 표현하면 다음과 같습니다.
function getArrayLength<T>(arr: T[]): number {
return arr.length;
}
console.log(getArrayLength<number>([1, 2, 3])); // 3
console.log(getArrayLength<string>(["hello", "world"])); // 2
이처럼 제네릭을 나타내는 식별자(일반적으로 T, U, V 를 사용)를 꺽쇠(<>) 안에 명시하여 함수명 뒤에 적어줍니다. 구분자는 콤마(,) 입니다.
함수를 호출할 때, 사용할 타입을 넘겨줄 수 있습니다.
function getData<T>(value: T): T {
return value;
}
const printData = <T, U>(a: T, b: U): void => {
console.log(a, b);
};
getData<number>(1);
printData<number, string>(1, "hello");
또한 제네릭 기본 값을 설정할 수 있습니다. default 타입을 지정하는 것으로, 타입을 명시하지 않고 함수 호출 시 기본 값으로 설정한 타입으로 지정됩니다.
const makeArr = <X, Y = string>(x: X, y: Y): [X, Y] => {
return [x, y];
};
const arr1 = makeArr<number>(4, "hello"); // [4, 'hello']
const arr2 = makeArr<number, boolean>(4, true); // [4, true]
📌 Generics - 인터페이스 & Type Alias
제네릭은 인터페이스와 타입 별정에도 자주 쓰입니다.
interface Vehicle<T> {
name: string;
color: string;
option: T;
}
const car: Vehicle<{ price: number }> = {
name: "Car",
color: "red",
option: {
price: 1000,
},
};
const bike: Vehicle<boolean> = {
name: "Bike",
color: "blue",
option: true,
};
type Media<T> = {
name: string;
option: T;
}
let book: Media<boolean> = {
name: 'Book',
option: true
}
📌 Generics - 클래스
클래스에서도 제네릭을 사용하여 유연하게 클래스를 다룰 타입을 지정할 수 있습니다.
제네릭을 클래스에서 사용할 때 주의해야할 점은, static 정적 멤버는 제네릭으로 관리할 수 없다는 점입니다.
class Post<T> {
id: T;
title: string;
constructor(id: T, title: string) {
this.id = id;
this.title = title;
}
}
let post1 = new Post<number>(1, 'post1');
let post2 = new Post<string>('id2', 'post1');
📌 Generics - 제약 조건
제네릭 사용 시, extends 키워드를 사용하여 제네릭에 적용되는 타입의 종류를 제한할 수 있습니다.
<T extends K> 형태의 제네릭은 T 가 K 에 할당 가능해야함을 의미합니다.
다음은 제네릭 T 에 대해 number 또는 string 타입만을 허용하는 코드입니다. 이외의 타입에 대해 함수 호출 시 에러가 발생합니다.
function func<T extends (string | number)>(a: T) {
console.log(a);
}
func(1);
func("a");
func(true); // Error: 'boolean' 형식의 인수는 'string | number' 형식의 매개 변수에 할당될 수 없습니다.
✅ 속성 제약조건
예시로, .firstName 과 .lastName 을 프로퍼티로 갖는 객체를 매개변수로 받아, 풀네임으로 반환하는 함수를 작성한다고 가정합시다.
다음과 같이 작성하게 되면, T에 firstName, lastName 속성이 없다는 오류가 발생합니다. 컴파일러 입장에선 T 타입이 어떤 타입인지 모르기 때문입니다.
// 에러
// 'T' 형식에 'firstName' 속성이 없습니다.
// 'T' 형식에 'lastName' 속성이 없습니다.
const getFullName = <T>(obj: T): string => {
return obj.firstName + obj.lastName;
};
이때 extends 키워드를 사용하여, 제네릭 타입의 프로퍼티를 반드시 해당 속성을 포함하도록 지정해주어야 합니다.
하나라도 포함이 안 될 경우, 오류가 발생합니다.
const getFullName = <T extends { firstName: string; lastName: string }>(
obj: T
): string => {
return obj.firstName + obj.lastName;
};
let obj1 = {
firstName: "A",
lastName: "B",
};
let obj2 = {
firstName: "A",
lastName: "B",
option: true,
};
let obj3 = {
firstName: "A",
};
getFullName(obj1);
getFullName(obj2);
getFullName(obj3); // Error
✅ keyof
keyof 연산자는 객체 형태의 타입을 속성들만 뽑아 모아 union 타입으로 만들어줍니다.
다음 코데에서 Generic U 는 Generic T 의 속성의 키 값 중 하나여야 합니다. 때문에 키 값이 아닌 'z' 를 매개 변수로 넣었을 경우 에러가 발생합니다.
const getProperty = <T extends object, U extends keyof T>(obj: T, key: U) => {
return obj[key];
};
getProperty({ a: 1, b: 2, c: 3 }, "a");
// Error: '"z"' 형식의 인수는 '"a" | "b" | "c"' 형식의 매개 변수에 할당될 수 없습니다.
getProperty({ a: 1, b: 2, c: 3 }, "z");
'Programming Language > TypeScript' 카테고리의 다른 글
[TypeScript] 타입으로 변환하기 typeof & keyof (0) | 2024.01.19 |
---|---|
[TypeScript] 타입 단언과 타입 가드 (Type Assertions & Guard) (0) | 2024.01.16 |
[TypeScript] Call Signature 와 Index Signature (0) | 2023.12.26 |
[TypeScript] Type Alias VS Interface (2) | 2023.12.26 |
[TypeScript] 타입 선언 및 종류 (0) | 2023.12.24 |