타입 확장은 기존 타입을 사용해서 새로운 타입을 정의하는 것을 말한다. 기본적으로 타입스크립트에서는
interface
,type
키워드를 사용해서 타입을 정의한다. 뿐만 아니라 타입을 받아오는제네릭
과클래스
에서도 타입을 확장하거나 제한할 수 있다
TypeScript의 type 키워드를 사용하여 정의된 고정된 타입입니다.
확장 혹은 제한 방식 : 유니온 타입(Union Type), 인터섹션 타입(Intersection Type), 타입 별칭(Type Alias) 등을 사용
Partial
, Pick
, Omit
, Exclude
등)로 타입을 조작하고 싶을 때.type Animal = {
name: string;
};
type Dog = Animal & { breed: string }; // 인터섹션 타입으로 Animal을 확장
type Pet = Animal | Dog; // 유니온 타입으로 Animal과 Dog를 조합
// 타입 제한: 특정 속성 제거
type Person = {
name: string;
age: number;
location: string;
};
type PersonWithoutLocation = Omit<Person, 'location'>; // location 속성 제거
인터페이스는 TypeScript의 컨텍스트 내에서만 존재하는 가상 구조입니다. TypeScript 컴파일러는 타입 체크 목적으로만 인터페이스를 사용합니다.
- 인터페이스의 확장방식 : 상속 , 선언적 확장
✚ 인터페이스는 추상클래스로 사용됩니다. 메소드의 형태만 선언해서 인터페이스를 정의하고, 이후에 클래스를 정의할 때 implements 키워드를 사용하면서 이 인터페이스를 지정하면, 이 클래스는 추상함수로 선언된 메소드를 받아들여야 합니다.
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
const myDog: Dog = {
name: "Buddy",
breed: "Golden Retriever",
};
// 인터페이스 확장
interface Person {
name: string;
age: number;
}
// 선언적 확장: 기존 인터페이스에 속성 추가
interface Person {
location: string;
}
const person: Person = {
name: "Alice",
age: 30,
location: "New York",
};
제네릭은 타입을 파라미터화하여 유연하게 여러 타입을 처리할 수 있도록 합니다.
- 제네릭의 타입 확장과 제한 :
extends
키워드
extends
, T extends keyof U
등).동적으로 결정
되어야 할 때.// 제네릭으로 타입을 제한
function getValue<T extends { name: string }>(obj: T): string {
return obj.name;
}
getValue({ name: "Alice" }); // 정상
// getValue({ age: 30 }); // 오류: 'name' 속성이 없기 때문
// 제네릭을 사용한 인터페이스
interface Box<T> {
value: T;
}
const numberBox: Box<number> = { value: 123 };
const stringBox: Box<string> = { value: "Hello" };
클래스 상속은 새로운 클래스를 정의할 때 기존 클래스의 속성과 메서드를 재사용하여 확장하는 가장 기본적인 방법
interface Movable {
move(): void;
}
interface Barkable {
bark(): void;
}
class Dog implements Movable,Barkable {
name: string;
constructor(name: string) {
this.name = name;
}
move() {
console.log(`${this.name} is moving.`);
}
bark() {
console.log(`${this.name} barks.`);
}
}
const dog = new Dog('Buddy');
dog.move(); // Buddy is moving.
dog.bark(); // Buddy barks.
추상 클래스는 공통된 기능을 가진 클래스들의 기본 형태를 정의하고, 구체적인 구현은 서브클래스에서 하도록 강제하는 방식입니다.
통해 공통 기능을 공유
해야 할 때.abstract class Animal {
abstract makeSound(): void;
move() {
console.log('Moving...');
}
}
class Dog extends Animal {
makeSound() {
console.log('Woof!');
}
}
const dog = new Dog();
dog.makeSound(); // Woof!
dog.move(); // Moving...
도서관에서 책을 분류하고 관리하는 시스템을 설계함.
📚 장르 : 문학, 과학, 역사, 미술
💝 사은품 종류 : 책갈피, 포스터, 스티커
💰 결재 상태 : 결재 됨, 안됨
[타입 영역]
도서관의 책은 다양한 장르가 있으며 책마다 사은품이 있을 수도 있고 없을 수도 있다
[처리 영역]
사은품이 있는 경우→ 결제 시 계산 포스기에서 사은품을 선택할 수 있는 옵션이 나타나야 한다
계산이 완료된 책→ 계산 완료된 상품으로 표시되어야 하며, 이 정보는 포스 시스템에 반영
사용자는 결제 방법(카드, 현금 등)을 선택할 수 있으며, 결제 완료 후 사은품에 존재 여부에 따라 추가적인 결제 흐름과 마지막엔 가격이 나온다
타입 영역
장르와 사은품 결재 상태는 특정형태로 유지되어서 타입을 사용
// 책의 장르를 정의
type Genre = '문학' | '과학' | '역사' | '미술';
// 사은품 종류를 정의
type GiftType = '책갈피' | '포스터' | '스티커';
// 결제 상태를 정의
type PaymentStatus = 'paid' | 'unpaid';
인터페이스(Interface) 영역
인터페이스는 책의 구조를 정의하며, 사은품의 유무와 결제 상태와 같은 속성을 추가로 확장하여 사용할 수 있다.
// 책의 구조 정의 - 사은품과 결제 상태를 포함하여 타입 제한
interface Book {
title: string;
genre: Genre;
price: number;
gift?: GiftType; // 사은품은 있을 수도, 없을 수도 있음
status: PaymentStatus;
}
// 결제 처리와 관련된 인터페이스
interface Payable {
calculatePrice(): number;
showPaymentOptions(): void;
}
제네릭 클래스 영역
제네릭을 사용하여 사은품의 유무와 결제 여부를 처리한다. : 제네릭을 통해 다양한 결제 상태와 사은품 여부를 쉽게 확장하거나 조합
추상 클래스는 기본적으로 책과 관련된 메서드를 정의하며, 실제 계산 로직은 서브클래스에서 구현
// 추상 클래스 정의 - 결제 로직
// payable + Book타입을 확장하는 T
abstract class LibraryItem<T extends Book> implements Payable {
protected book: T;
constructor(book: T) {
this.book = book;
}
// 입장 조건: 모든 서브클래스는 이 메서드를 구현해야 함
abstract calculatePrice(): number;
showPaymentOptions(): void {
console.log('카드와 신용카드로 결제가 가능합니다 ');
}
showPaidStatus() {
if (this.book.status === 'paid') {
console.log('결제가 이미 완료됨');
} else {
console.log('결제 아직 안됨');
}
}
}
// 제네릭 클래스 - 사은품과 결제 여부에 따른 처리
class ProcessBook<T extends Book> extends LibraryItem<T> {
// 입장 조건: Book 타입을 확장하는 T 타입을 처리
calculatePrice(): number {
if (this.book.gift) { //추상클래스 사용
console.log(`사은품포함 : ${this.book.gift}`);
return this.book.price + 5; // 사은품이 있을 경우 추가 비용
}
return this.book.price;
}//사은품에 따른 가격 수정
displayBookDetails() {
console.log(`Title: ${this.book.title}, Genre: ${this.book.genre}`);
this.showPaidStatus();
}
}
// 특정 책 인스턴스 생성 및 처리
const myBook: Book = {
title: '노인과바다',
genre: '문학',
price: 20,
gift: '책갈피',
status: 'paid',
};
//LibraryItem의 생성자 호출: ProcessBook 클래스는 LibraryItem 추상 클래스를 상속받아
//ProcessBook의 생성자는 먼저 LibraryItem의 생성자를 호출하여 this.book을 초기화
//LibraryItem의 생성자에서 전달된 myBook -> this.book에 할당.
const processedBook = new ProcessBook(myBook);
processedBook.displayBookDetails(); // 책 정보 및 결제 상태 표시
console.log(`Total price: ${processedBook.calculatePrice()}원`); // 총 금액 계산
타입(Type) 확장 방식으로 사용되는 것 중 맞는 것은?
제네릭(Generic)의 타입에서 extends
키워드의 역할은 무엇인가요?