brunch

You can make anything
by writing

C.S.Lewis

by 훈오빵 Nov 27. 2023

Typescript 공부(2)

 III. Interface & Class


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 제어자로 정의된 변수에는 접근 불가



                    

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari