1. Type 별칭과 Interface 비교
- Interface type을 선언하는 문법은 객체 type 별칭을 만드는 문법과 비슷. 인터페이스는 type을 사용한 객체의 별칭 타입과 비교해 더 읽기 쉬운 오류 메시지, 더 빠른 컴파일러 성능, 클래스와 함께 사용할 수 있는 장점 제공
- type vs. interface
기존 타입 또는 새 타입을 생성하는데 사용 vs. 객체 타입의 구조를 정의하는데 사용
다른 타입 또는 인터페이스를 상속하거나 구현할 수 없음 vs. 다른 인터페이스를 상속하거나 구현할 수 있음
리터릴 타입, 유니온 타입, 인터섹션 타입을 사용할 수 있음 vs. extends 키워드로 인터페이스 확장이 가능
간단한 타입 별칭을 생성할 때 적합 vs. 잘 정의된 구조의 객체 타입을 정의할 때 적합
type BookType = { // BookType
title: string;
price: number;
author: string;
};
interface Book { // Book interface
title: string;
price: number;
author: string;
};
let bookType: BookType = { // BookType 객체 할당
title: "훈민정음",
price: 10000,
author: "세종대왕",
};
let book: Book = { // Book 인터페이스 객체 할당
title: "훈민정음",
price: 10000,
author: "세종대왕",
};
2. Interface의 선택적 속성과 읽기 전용 속성
interface Car {
name: string;
price: number;
brand: string;
options?: string[]; // 차량의 옵션은 선택적 속성
}
let avante: Car = { // 아반떼에는 에어컨과 내비게이션의 옵션이 있음
name: "아반떼",
price: 1500,
brand: "현대",
options: ["에어컨", "내비게이션"],
};
let morning: Car = { // 모닝은 아무런 옵션이 없음
name: "모닝",
price: 700,
brand: "KIA",
};
** 속성에 '?'를 붙이면 선택적 속성
** 모닝에는 아무런 옵션을 주지 않았는데, options는 선택적 속성이므로 에러가 나지 않음
interface Citizen { // 시민을 의미하는 인터페이스 정의
id: string;
name: string;
region: string;
readonly age: number; // 나이는 변경할 수 없음
}
let hoon: Citizen = { // Citizen 인터페이스 객체 생성
id: "123456",
name: "훈",
region: "경기도",
age: 40,
};
hoon.age = 41; // age 속성은 읽기 전용(read-only)이므로 에러
3. Interface 확장하기
interface WebtoonCommon { // 공통으로 사용할 인터페이스
title: string;
createdDate: Date;
updatedDate: Date;
}
interface Episode extends WebtoonCommon { // episode interface
episodeNumber: number;
seriesNumber: number;
}
interface Series extends WebtoonCommon { // series interface
seriesNumber: number;
author: string;
}
const episode: Episode = { // episode 객체
title: "세계여행 지구마블 1화",
createdData: new Date(),
updatedData: new Date(),
episodeNumber: 1,
seriesNumber: 123,
};
const series: Series = { // series 객체
title: "세계여행 지구마블",
createdDate: new Date(),
updatedDate: new Date(),
seriesNumber: 123,
author: "노홍철",
};
** WebtoonCommon은 공통으로 사용할 인터페이스
4. Interface 병합
- 같은 이름의 인터페이스가 있으면 에러가 나지 않고, 각각 속성이 다르다면 내부적으로 병합됨.
interface Clock {
time: Date;
}
interface Clock {
brand: string;
}
interface Clock {
price: number;
}
const wrongClock: Clock = { // brand, price 속성이 없어서 에러
time: new Date(),
};
const clock: Clock = { // Clock interface 병합
time: new Date(),
brand: "Rolex",
price: 10000,
};
** interface는 병합되어서 내부적으로는 다음과 같이 선언한 것과 같음
interface Clock {
time: Date;
brand: string;
price: number;
}
5. Class의 method와 속성
class Hello { // class 선언부
// 생성자 메서드
constructor( ) {
this.sayHello("created");
}
// 메서드
sayHello(message: string) {
console.log(message);
}
}
// Hello 클래스의 인스턴스 생성
const hello = new Hello();
hello.sayHello("안녕하세요~")
-----------------------------------------------------
(결과 출력)
created <- 생성자에서 실행
안녕하세요~ <- hello.sayHello("안녕하세요~")의 결과
class Rectangle {
width: number; // 클래스 변수, 가로를 의미
height: number; // 클래스 변수, 세로를 의미
// 클래스 생성 시 가로, 세로 값을 넣어줌
constructor(width: number, height: number) {
// this.width는 클래스 변수이며 width는 매개변수로 받은 값을 담은 변수
this.width = width;
this.height = height;
}
// 반환 타입은 number 타입
getArea() {
return this.width * this.height;
}
}
// 클래스 인스턴스 생성
const rectangle = new Rectangle(10, 5);
// getArea() 메서드 실행
rectangle.getArea();
------------------------------------------------------
(결과 출력)
50
6. Interface를 구현한 Class
- Class에서 interface를 상속할 때 implements 키워드 드사용
interface IClicker {
count: number;
click(): number;
}
class Clicker implements IClicker {
// 인터페이스를 상속받고 인터페이스 속성을 구현하지 않아서 에러 러발생
}
interface IClicker {
count: number;
click(): numner
}
class Clicker implements IClicker {
// count 기본값(0)을 설정
count: number = 0;
click(): number {
this.count += 1
console.log(`Click! [count] : ${this.count}`);
return this.count;
}
}
const clicker = new Clicker();
clicker.click(); // Click! [count] : 1
clicker.click(); // Click! [count] : 2
clicker.click(); // Click! [count] : 3
** 인터페이스는 클래스 구현을 강제하기 기위한 용도로도 사용 가능
7. 추상 Class
- 추상 클래스는 abstract 키워드가 붙어있는 클래스로 추상 메서드를 가지고 있는 클래스. 추상 메서드는 메서드의 구현체가 가없는 메서드. 추상 메서드를 사용하면 반드시 구현해야 하는 기능을 강제할 수 있어 깜빡하고 빼먹는 실수를 예방할 수 있음.
abstract class Logger { // abstract 키워드가 있으면 추상 클래스
prepare() {
console.log("====================");
console.log("로그를 를남기기 위한 준비");
}
// 로그를 남기는 절차를 정의한 메서드
log(message: string) {
this.prepare();
this.execute(message);
this.complete();
};
// 추상 메서드
abstract execute(message: string): void;
complete() {
console.log("작업 완료");
console.log("");
}
}
// 추상 클래스는 상속해 사용
class FileLogger extends Logger {
filename: string;
// 상속을 받은 경우, 기본 생성자가 아니라면 super()를 먼저 실행
constructor(filename: string) {
super();
this.filename = filename;
}
// 추상 매서드 구현
execute(message: string): void {
// 파일에 직접 쓰지는 않지만 쓴다고 가정
console.log(`[${this.filename}] > `, message);
}
}
class ConsoleLogger extends Logger {
// 추상 메서드 구현
execute(message: string): void {
console.log(message);
}
}
const fileLogger = new FileLogger("test.log");
fileLogger.log("파일에 로그 남기기 테스트");
const consoleLogger = new ConsoleLogger();
consoleLogger.log("로그 남기기");
====================================== <- fileLogger.log() 실행 결과
로그를 남기기 위한 준비
[test.log] > 파일에 로그 남기기 테스트
작업 완료
====================================== <- consoleLogger.log() 실행 결과
로그를 남기기 위한 준비
로그 남기기
작업 완료
8. Class의 접근 제어자
- 접근 제어자 : 설명
public : 모든 곳에서 접근 가능(기본값)
protected : 클래스 내부 혹은 자녀 클래스에서만 접근 가능
private : 클래스 내부에서만 접근 가능
// 부모 클래스
class Parent {
openInfo = "공개 정보";
protected legacy = "유산";
private parentSecret = "부모의 비밀 정보";
// private 정보에 접근 가능
checkMySecret() {
console.log(this.parentSecret);
}
}
// 자녀 클래스, 부모 상속
class Child extends Parent {
private secret = "자녀의 비밀 정보";
// 자녀는 부모의 protected 확인 가능
checkLegacy() {
console.log(super.legacy);
}
// 부모의 private 변수에는 접근 불가능
checkParentSecret() {
console.log(super.parentSecret);
}
}
class Someone {
checkPublicInfo() {
const p = new Parent();
// 다른 클래스가 public 변수 접근 가능
console.log(p.openInfo);
// protected와 private는 접근 불가능
console.log(p.legacy);
console.log(p.parentSecret)
}
}
** 자녀 클래스는 부모의 보호된 변수인 protected에 접근 가능
** private 제어자로 정의된 변수에는 접근 불가