반응형
Class
타입스크립트에서는 객체 지향 프로그래밍을 지원하는 기능을 제공하고 있다. 추상 클래스와 상속을 통해 코드의 재사용성을 높이고, 구조화된 방식으로 프로그램을 설계할 수 있다. 접근 제한자를 사용하여 클래스의 속성과 메서드의 접근 범위를 제어할 수 있으며, 이를 통해 코드의 캡슐화를 구현할 수 있다.
실습하기
// 'Words' 타입 정의: 문자열 키와 문자열 값을 가지는 객체 타입
type Words = {
[key: string]: string; // 인덱스 시그니처: 키는 문자열, 값은 문자열
};
// 'Dict' 클래스 정의
class Dict {
private words: Words; // private 속성 'words', 'Words' 타입 사용
constructor() {
this.words = {}; // 빈 객체로 초기화
}
// 단어 추가 메서드
add(word: Word) {
// 'words' 객체에 단어가 없으면 추가
if (this.words[word.term] === undefined) {
this.words[word.term] = word.def;
}
}
// 단어 정의 반환 메서드
def(term: string) {
// 'words' 객체에서 해당 단어의 정의 반환
return this.words[term];
}
// 단어 정의 업데이트 메서드
update(word: Word) {
// 'words' 객체에 단어가 있으면 정의 업데이트
if (this.words[word.term] !== undefined) {
this.words[word.term] = word.def;
}
}
// 단어 삭제 메서드
del(term: string) {
// 'words' 객체에서 단어가 있으면 삭제
if (this.words[term] !== undefined) {
delete this.words[term];
}
}
}
// 'Word' 클래스 정의
class Word {
// 생성자: 'term'과 'def' 속성을 초기화
constructor(public term: string, public def: string) { }
}
// 단어 객체 생성
const kimchi = new Word("kimchi", "Korean food");
const pizza = new Word("pizza", "piazza");
// 사전 객체 생성
const dict = new Dict();
// 단어 추가
dict.add(kimchi);
dict.add(pizza);
// 단어 정의 출력
console.log("KIMCHI:", dict.def("kimchi")); // "KIMCHI: Korean food"
console.log("PIZZA:", dict.def("pizza")); // "PIZZA: piazza"
// 단어 정의 업데이트
dict.update(new Word("kimchi", "very incredible Korean food"));
// 업데이트된 단어 정의 출력
console.log("UPDATE KIMCHI:", dict.def("kimchi")); // "UPDATE KIMCHI: very incredible Korean food"
console.log("NOT UPDATE PIZZA:", dict.def("pizza")); // "NOT UPDATE PIZZA: piazza"
// 단어 삭제
dict.del("pizza");
// 단어 삭제 후 정의 출력
console.log("DELETE PIZZA", dict.def("pizza")); // "DELETE PIZZA undefined"
console.log("NOT DELETE KIMCHI:", dict.def("kimchi")); // "NOT DELETE KIMCHI: very incredible Korean food"
Interfaces
TypeScript에서는 인터페이스를 사용하여 객체의 구조를 정의하고, 클래스가 이를 구현하도록 강제할 수 있다.
반면, JavaScript에는 이러한 기능이 없고, 객체의 구조를 강제할 방법이 없다.
Interface와 type의 차이점
interface는 주로 객체의 구조를 정의하고 확장과 병합이 가능한 반면, type은 다양한 형태의 타입을 정의할 수 있으며 유니언(| 타입을 사용)과 인터섹션 타입(& 타입을 사용) 표현이 가능하다.
// 첫 번째 'Animal' 인터페이스 정의
interface Animal {
name: string; // 'name' 속성: 동물의 이름
}
// 두 번째 'Animal' 인터페이스 정의 (병합)
interface Animal {
age: number; // 'age' 속성: 동물의 나이
}
// 세 번째 'Animal' 인터페이스 정의 (병합)
interface Animal {
species: string; // 'species' 속성: 동물의 종
}
// 'Animal' 인터페이스를 구현하는 클래스
class Dog implements Animal {
constructor(
public name: string, // 'name' 속성 초기화
public age: number, // 'age' 속성 초기화
public species: string // 'species' 속성 초기화
) { }
// 메서드: 'Dog' 객체의 정보를 출력
getInfo() {
return `${this.name} is a ${this.age}-year-old ${this.species}.`;
}
}
// 'Animal' 인터페이스를 사용하는 객체 생성
const myDog: Animal = {
name: "Buddy", // 이름 설정
age: 5, // 나이 설정
species: "Labrador" // 종 설정
};
// 'Dog' 클래스의 인스턴스 생성
const buddy = new Dog("Buddy", 5, "Labrador");
// 정보 출력
console.log(buddy.getInfo()); // "Buddy is a 5-year-old Labrador."
// 인터페이스 병합 결과 확인
console.log(myDog); // { name: "Buddy", age: 5, species: "Labrador" }
Polymorphism(다형성)
제네릭을 사용한 다형성은 다양한 타입에 대해 동일한 로직을 적용할 수 있게 도와주는데, 아래 코드에서는 LocalStorage 클래스가 제네릭을 사용하여 문자열, 불리언 등 다양한 타입의 데이터를 저장, 읽기, 업데이트, 삭제할 수 있는 유연한 구조를 제공하고 있다. TS에서는 제네릭을 통해 class에 대해서도 다형성을 통해 코드의 재사용성, 타입 안전성, 유연성을 높일 수 있다.
// 제네릭 인터페이스 SStorage 정의: 키는 문자열, 값은 제네릭 타입 T
interface SStorage<T> {
[key: string]: T; // 인덱스 시그니처: 모든 키는 문자열이고, 값은 타입 T
}
// 제네릭 클래스 LocalStorage 정의: 제네릭 타입 T 사용
class LocalStorage<T> {
// SStorage<T> 타입의 storage 속성 초기화
private storage: SStorage<T> = {};
// Create: 새로운 항목을 추가
set(key: string, value: T) {
if (this.storage[key] !== undefined) {
// 키가 이미 존재하면 메시지 출력
return console.log(`${key}가 이미 존재합니다. update 호출 바랍니다.`);
}
// 키가 존재하지 않으면 새로운 값 저장
this.storage[key] = value;
}
// Read: 키에 해당하는 값을 반환
get(key: string): T | void {
if (this.storage[key] === undefined) {
// 키가 존재하지 않으면 메시지 출력
return console.log(`${key}가 존재하지 않습니다.`);
}
// 키가 존재하면 해당 값 반환
return this.storage[key];
}
// Update: 기존 항목을 업데이트, 존재하지 않으면 새로 추가
update(key: string, value: T) {
if (this.storage[key] !== undefined) {
// 키가 존재하면 값 업데이트
this.storage[key] = value;
} else {
// 키가 존재하지 않으면 메시지 출력 후 새로 추가
console.log(`${key}가 존재하지 않아 새로 만듭니다.`);
this.storage[key] = value;
}
}
// Delete: 키에 해당하는 값을 삭제
remove(key: string) {
if (this.storage[key] === undefined) {
// 키가 존재하지 않으면 메시지 출력
return console.log(`${key}가 존재하지 않습니다.`);
}
// 키가 존재하면 해당 값 삭제
delete this.storage[key];
}
// Clear: 모든 항목 삭제
clear() {
this.storage = {}; // storage 객체 초기화
}
}
// 문자열을 저장할 수 있는 LocalStorage 인스턴스 생성
const stringsStorage = new LocalStorage<string>();
// 키 "asdf"에 해당하는 값 읽기 시도 (값이 없으므로 메시지 출력)
stringsStorage.get("asdf");
// 키 "asdf"에 문자열 "asdfasf" 저장
stringsStorage.set("asdf", "asdfasf");
// 불리언을 저장할 수 있는 LocalStorage 인스턴스 생성
const booleanStorage = new LocalStorage<boolean>();
// 키 "asdf"에 불리언 값 false 저장
booleanStorage.set("asdf", false);
반응형