티스토리 뷰
1️⃣ Generic 이란?
정적 type 언어는 클래스나 함수를 정의할 때 type을 선언해야 한다.
예를 들어, C언어는 int type 변수를 선언하면 정수형 값만 할당할 수 있다.
Generic은 코드를 작성할 때가 아니라 코드가 수행될 때 타입을 명시한다.
(클래스나 메서드에서 사용할 내부 데이터 type을 외부에서 지정하여 정의 시점이 아니라 생성 시점에 타입을 명시할 수 있게함 → 코드의 재사용성이 높아짐)
코드를 작성할 때 식별자를 써서 아직 정해지지 않은 타입을 표시한다.
- 일반적으로 식별자는 T, U, V, ...를 사용
- 필드 이름의 첫 글자를 사용하기도 함
▶ Generic을 사용하는 이유
1) 재사용성이 높은 함수와 클래스 생성 가능
- 여러 타입에서 동작이 가능하다. (한 번의 선언으로 다양한 타입에 재사용할 수 있다.)
- 코드의 가독성이 향상된다.
2) 오류를 쉽게 포착 가능
- any 타입을 사용하면 컴파일 시 타입을 체크하지 않는다 → 타입을 체크하지 않아 관련 메소드의 힌트를 사용할 수 없다 → 컴파일 시에 컴파일러가 오류를 찾지 못한다
- Generic도 any처럼 미리 타입을 지정하지는 않지만, 타입을 체크해 컴파일러가 컴파일 시점에서 오류를 찾을 수 있다.
→ 따라서, 타입스크립트에서는any타입을 지양하고, 다양한 타입 지정이 필요할 때는 generic 또는 union type을 권장한다.
2️⃣ Generic으로 함수와 클래스 만들기
generic을 활용한 함수
function sort<T>(items: T[]): T[] {
return items.sort();
}
const nums: number[] = [1, 2, 3, 4];
const chars: string[] = ["a", "b", "c", "d"];
sort<number>(nums);
sort<string>(chars);
generic을 활용한 class
class Queue<T> {
protected data: Array<T> = [];
push(item: T) {
this.data.push(item);
}
pop(): T | undefined {
return this.data.shift();
}
}
const numberQueue = new Queue<number>();
numberQueue.push(0);
numberQueue.push("1"); // Error 발생 -> 의도하지 않은 실수를 사전 검출 가능
3️⃣ Union type
"|"를 사용해 두 개 이상의 타입을 선언하는 방식
Union과 Generic 모두 여러 타입을 다룰 수 있다.
- Union은 선언한 공통된 메소드만 사용 가능
- 리턴 값이 하나의 타입이 아닌 선언된 Union 타입으로 지정
const printMessage = (message: string | number) => {
return message;
}
const message1 = printMessage(1234);
const message2 = printMessage("hello world !");
message1.length; // error: length does not exist on type string | number
// -> string과 number type의 공통된 메소드만 사용 가능하다
위 코드를 generic으로 쓴다면
const printMessage2 = <T>(message: T) => {
return message;
}
const message1 = printMessage2<string>("hello world !");
message1.length; // 위 union type 사용 시와 달리 error 발생하지 않음
4️⃣ 제약조건 (Constraints / keyof)
원하지 않는 속성에 접근하는 것을 막기 위해 Generic에 제약조건을 사용한다.
extends키워드를 사용하여 특정 타입들로만 동작하는 제네릭 함수를 만들고 싶을 때 사용한다.
1) Constraints
: 특정 타입들로만 동작하는 Generic 함수를 만들 때 사용
- Generic T에 제약 조건을 설정한다. (문자열 or 숫자)
- 제약 조건을 벗어나는 타입을 선언하면 에러가 발생한다.
const printMessage = <T extends string | number>(message: T): T => {
return message;
}
printMessage<string>("1");
printMessage<number>(1);
printMessage<boolean>(false) // Error: Type 'boolean' does not satisfy the constraint 'string | number'.
2) Keyof
: 두 객체를 비교할 때 사용한다.
- Generic T는 키 값이 a, b, c만 존재하는 object이다.
- U의 값인 'z'가 Generic T의 키 값 중 존재하지 않기 때문에 오류가 발생한다.
const getProperty = <T extends object, U extends keyof T>(obj: T, key: U) => {
return obj[key]
}
getProperty({ a: 1, b: 2, c: 3 }, "a");
getProperty({ a: 1, b: 2, c: 3 }, "z"); // error: Argument of type '"z"' is not assignable to parameter of type '"a" | "b" |“c"'
5️⃣ 디자인 패턴 (Factory Pattern with Generics)
- 객체를 생성하는 인터페이스는 미리 정의하되, 인스턴스를 만들 클래스의 결정은 서브 클래스에서 진행하는 패턴
- 여러 개의 서브 클래스를 가진 슈퍼 클래스가 있을 때, 입력에 따라 하나의 서브 클래스의 인스턴스를 반환
Factory Pattern
interface Car {
drive(): void
park(): void
}
class Bus implements Car {
drive(): void {}
park(): void {}
}
class Taxi implements Car {
drive(): void {}
park(): void {}
}
class CarFactory { // 실제로 객체를 생성시키는 클래스
static getInstance(type: string): Car { // 전역멤버로 구현, 생성시키고자 하는 클래스를 인수값(type)으로 전달
switch (type) {
case "bus":
return new Bus();
default:
return new Taxi();
}
}
}
const bus = CarFactory.getInstance("bus");
const taxi = CarFactory.getInstance("taxi");
위 코드는 car의 type이 추가 될 때마다 case 문을 추가 해야하는 단점이 있다.
위 코드를 Generics을 활용한 Factory Pattern으로 바꾸면 아래와 같다.
interface Car {
drive(): void
park(): void
}
class Bus implements Car {
drive(): void {}
park(): void {}
}
class Taxi implements Car {
drive(): void {}
park(): void {}
}
class Suv implements Car {
drive(): void {}
park(): void {}
}
export class CarFactory {
static getInstance<T extends Car>(type: { new (): T }): T {
return new type();
}
}
const bus = CarFactory.getInstance(Bus);
const taxi = CarFactory.getInstance(Taxi);
이 글은 엘리스의 AI트랙 5기 강의를 들으며 정리한 내용입니다.
'개발공부 > 🟦 TypeScript' 카테고리의 다른 글
[TS] Interface (0) | 2022.11.07 |
---|---|
[TS] 객체 지향 프로그래밍 (OOP) (0) | 2022.10.26 |
[TS] TypeScript를 이용해 함수 사용하기 (0) | 2022.10.09 |
[TS] 타입스크립트의 기본 Type (0) | 2022.10.09 |
[TS] 타입스크립트를 사용하는 이유 (0) | 2022.10.09 |
프론트엔드 개발자 삐롱히의 개발 & 공부 기록 블로그