배꼽파지 않도록 잘 개발해요

[엘리스sw] 5주차 3일 - 타입스크립트 개념, 클래스 본문

교육/엘리스 SW 트랙

[엘리스sw] 5주차 3일 - 타입스크립트 개념, 클래스

꼽파 2024. 1. 25. 12:03


◆ TypeScript 기본

Class


타입스크립트 개념

타입스크립트 개요

타입스크립트
· Microsoft에서 개발한 오픈 소스 언어
· 자바스크립트에 JavaScript에 정적 타이핑을 추가한 언어로, 자바스크립트의 최상위 집합
· 자바스크립트로 변환해주는 컴파일러가 필요함.

타입스크립트를 사용하는 이유
· 동적 타입을 정적으로 선언할 수 있음.
· 타입 유추를 통한 타입 제어가 가능함.
· 자바스크립트의 런타임 단계에서 발생하는 타입 에러는 타입스크립트를 이용한다면 컴파일 단계에서 미리 확인하고 고칠 수 있음.
· JavaScript에서 찾을 수 없는 추가 코드 기능을 제공함.

 

자바스크립트

let a;

a = 1;
a = '1';

console.log(a)  // 1
const sum = (a, b) => {
    return a + b;
}

console.log(sum(1, "2"));  // 12

 

타입스크립트

let a : number;

a = 1;
a = '1';
// main.ts(4,1): error TS2322: Type 'string' is not assignable to type 'number'.
const sum = (a: number, b: number) => {
    return a + b;
}

console.log(sum(1, 2));  // 3

타입스크립트에서는 매개변수 타입을 제어할 수 있어서 함수 아규먼트로 숫자를 전달하면 결과값으로 숫자를 전달받을 수 있다는 것을 명시할 수 있음.


TypeScript의 기본 Type

TypeScript는 JavaScript의 코드에 변수나 함수 등 Type을 정의할 수 있음.
Type을 나타내기 위해서 Type Annotation(타입 표기)를 사용한다.

TypeScript의 Type

  • 기본 자료형(primitive type) : string, boolean, number, null, undefined, symbol(ES6 추가)
  • 참조 자료형(reference type) : object, array, function
  • 추가 제공 자료형 : tuple, enum, any, void, never

 

기본 자료형 (primitive type)
- object와 reference 형태가 아닌 실제 값을 저장하는 자료형
- primitive type 내장 함수를 사용할 수 있는 것은 자바스크립트 처리 방식 덕분임

 

string

let str: string = "hello!";

 

Boolean

let isTrue: boolean = true;

 

number
부동 소수값을 저장하는 타입
2진수, 8진수, 10진수, 16진수 사용 가능

let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

 

null
값이 의도적으로 비어 있는 상태

let n: null = null;

 

undefined
어떤 값도 할당되지 않은 상태

let u: undefined = undefined;
console.log(null === undefined)  // false
console.log(null == undefined)  // true
console.log(null === null)  // true
console.log(null == null)  // true
console.log(!null)  // true
console.log(isNaN(1 + null))  // false
console.log(isNaN(1 + undefined))  // true
main.ts(6,19): error TS2365: Operator '+' cannot be applied to types '1' and 'null'.
main.ts(7,19): error TS2365: Operator '+' cannot be applied to types '1' and 'undefined'.

 


typeof로 데이터타입 확인

typeof null // 'object'
typeof undefined // 'undefined'

 

참조 자료형(reference type)
- 객체, 배열, 함수 등과 같은 Object 형식의 타입
- 메모리에 값을 주소로 저장하고, 출력 시 메모리 주소와 일치하는 값을 출력

 

object
기본 자료형에 해당하지 않는 타입
string, boolean, number, null, undefined를 제외한 타입

function create(o: object): void{}

create({prop: 0})  // 성공
create([1, 2, 3])  // 성공
create("string")  // error
create(false)  // error
create(42)  // error
create(null)  // error
create(undefined)  // error

 

array
배열을 저장하는 타입

let arr: number[] = [1, 2, 3]

// 아래와 같이 제네릭을 사용한 타입 표기 가능
let arr: Array<number> = [1, 2, 3]

 

추가 제공 자료형
- TypeScript에서 개발자의 편의를 위해 추가로 제공하는 타입

 

tuple
길이와 각 요소의 타입이 정해진 배열을 저장하는 타입

let arr: [string, number] = ["Hi", 6];

// 문자열에 쓰는 메소드 concat을 호출함
arr[1].concat("!");
// Property 'concat' does not exist on type 'number'.

// 정의하지 않은 index 호출 시 오류
arr[3] = 'hello';
// Tuple type '[string, number]' of length '2' has no element at index '3'.

 

enum
특정 값(상수)들의 집합을 저장하는 타입

enum Car {BUS, TAXI, SUV};

let bus: Car = Car.BUS;
let bus: Car = Car[0];  // 인덱스 번호로 접근

// 인덱스를 사용자 편의로 변경
enum Car { BUS = 1, TAXI = 2, SUV = 3 };
let taxi: String = Car[2];

enum Car { BUS = 2, TAXI, SUV };
let taxi: String = Car[3];

 

any
모든 타입을 저장 가능
컴파일 중 타입 검사를 하지 않음

let str: any = "hi";
let num: any = 10;
let arr: any = ["a", 2, true];

 

void
보통 함수에서 반환값이 없을 때, any의 반대 타입
변수에는 undefined와 null만 할당하고, 함수에는 반환값을 설정할 수 없는 타입

let unknown: void = undefined;

function sayHi(): void{
    console.log("hi");
}

 

never

발생할 수 없는 타입

- 항상 오류를 발생시키거나 절대 반환하지 않는 반환 타입

- 종료되지 않는 함수

// 종료되지 않는 함수
function neverEnd(): never {
    while(true) {}
}

// 항상 오류를 발생시키는 함수
function error(message: string): never {
    throw new Error(message);
}

 

 


Utility types

Utility types
· TypeScript는 공통 타입 변환을 용이하게 하기 위해 유틸리티 타입을 제공한다.
· 유틸리티 타입은 전역으로 사용 가능하다.

종류

  • Partial<T>, Readonly<T>
  • Record<K, T>, Pick<T,K>
  • Omit<T,K>, Exclude<T,U>, Extract<T,U>
  • NonNullable<T>, Parameters<T>, ConstructorParameters<T>
  • ReturnType<T>, Required<T>

 

Partial<T>

프로퍼티를 선택적으로 만드는 타입을 구성

주어진 타입의 모든 하위 타입 집합을 나타내는 타입을 반환

type PartialPerson = Partial<{ name: string; age: number }>;
const partialPerson1: PartialPerson = {}; 
const partialPerson2: PartialPerson = { name: 'John' }; 
const partialPerson3: PartialPerson = { age: 25 }; 
const partialPerson4: PartialPerson = { name: 'Jane', age: 30 };

위 네 가지 경우는 다 유효함.

 

Readonly<T>

프로퍼티를 읽기 전용(readonly)으로 설정한 타입 구성

type ReadonlyPerson = Readonly<{ name: string; age: number }>;

const readonlyPerson: ReadonlyPerson = { name: "John", age: 25 };

readonlyPerson.name = "Jane"; // 에러 (수정 불가)

 

Record<K,T>

프로퍼티 집합 K로 타입을 구성한다.

타입의 프로퍼티들을 다른 타입에 매핑시키는 데 사용한다.

type Person = { name: string; age: number };
type PersonRecord = Record<string, Person>;

const personRecord: PersonRecord = {
  john: { name: "John", age: 25 },
  jane: { name: "Jane", age: 30 },
};

레코드는 키-값 쌍이며, Record<string, Person>은 키가 문자열이고 값이 Person 유형인 레코드

 

Pick<T,K>

프로퍼티 K의 집합을 선택해 타입을 구성한다.

type Person = { name: string; age: number; address: string };
type PersonSubset = Pick<Person, "name" | "age">;

const personSubset: PersonSubset = { name: "John", age: 25 };
// personSubset는 선택한 속성만 포함

 

Omit<T,K>

모든 프로퍼티를 선택한 다음 K를 제거한 타입을 구성한다.

type Person = { name: string; age: number; address: string };
type PersonWithoutAddress = Omit<Person, "address">;

const personWithoutAddress: PersonWithoutAddress = { name: "John", age: 25 };
// personWithoutAddress는 "address" 속성이 없음.

 

Exclude<T,U>

T에서 U에 할당할 수 있는 모든 속성을 제외한 타입을 구성한다.

type Numbers = 1 | 2 | 3 | 4;
type EvenNumbers = Exclude<Numbers, 1 | 3>;

const evenNumbers: EvenNumbers = 2 | 4;
// evenNumbers에는 짝수만 포함

 

Extract<T,U>

T에서 U에 할당할 수 있는 모든 속성을 추출하여 타입을 구성한다.

type Numbers = 1 | 2 | 3 | 4;
type OddNumbers = Extract<Numbers, 1 | 3>;

const oddNumbers: OddNumbers = 1 | 3;
// oddNumbers에는 홀수만 포함

 

NonNullable<T>

null과 undefined를 제외한 타입

type NullableString = string | null | undefined;
type NonNullString = NonNullable<NullableString>;

const nonNullString: NonNullString = "Hello";
// nonNullString은 null 또는 undefined일 수 없음.

 

Parameters<T>

함수 타입 T의 매개변수 타입들의 튜플 타입을 구성한다.

type AddFunction = (a: number, b: number) => number;
type AddFunctionParameters = Parameters<AddFunction>;

const addFunctionParams: AddFunctionParameters = [1, 2];
// addFunctionParams는 두 개의 숫자로 이루어진 튜플

 

ConstructorParameters<T>

생성자 함수 타입의 모든 매개변수 타입을 추출한다.
모든 매개변수 타입을 가지는 튜플 타입(T가 함수가 아닌 경우 never)을 생성한다.

class Person {
  constructor(name: string, age: number) {}
}

type PersonConstructorParams = ConstructorParameters<typeof Person>;

const personParams: PersonConstructorParams = ["John", 25];
// personParams는 Person 생성자의 두 매개변수로 이루어진 튜플

 

ReturnType<T>

함수 T의 반환 타입으로 구성된 타입을 생성한다.

type GreetFunction = () => string;
type GreetFunctionReturnType = ReturnType<GreetFunction>;

const greeting: GreetFunctionReturnType = "Hello, World!";
// greeting은 GreetFunction의 반환 형식인 문자열

 

Required<T>

T의 모든 프로퍼티가 필수로 설정된 타입을 구성한다.

type PartialPerson = { name?: string; age?: number };
type RequiredPerson = Required<PartialPerson>;

const requiredPerson: RequiredPerson = { name: "John", age: 25 };
// requiredPerson은 모든 속성이 선택되지 않은 형태

https://www.typescriptlang.org/ko/docs/handbook/utility-types.html

 

Documentation - Utility Types

Types which are globally included in TypeScript

www.typescriptlang.org


TypeScript를 이용해 함수 사용하기

매개변수

function add(x, y) {
	return x + y;
}

add(2, 5);

 

  • 매개변수(parameter) : 함수를 정의할 때 사용되는 변수
    인자값 = 매개변수 = 파라미터
    add(x, y)에서 x, y
  • 인수(argument) : 함수를 호출할 때 사용되는 값
    add(2, 5)에서 2, 5

 

일급 객체(first-class object)

다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체

일급 객체의 조건

  • 다른 함수에 매개변수로 제공할 수 있다.
  • 함수에서 반환 가능하다.
  • 변수에 할당 가능하다.

JavaScript와 TypeScript의 함수는 일급 객체(first-class object)

 

함수 생성 방식 5가지

: 함수 선언식, 함수 표현식, 화살표 함수 표현식, 단축형 화살표 함수 표현식, 생성자 함수

 

함수 선언식

function world(name) {
	return `hello ${name}`;
}

 

함수 표현식

let world2 = function (name) {
	return `hello ${name}`
}

 

화살표 함수 표현식

let world3 = (name) => {
	return `hello ${name}`;
}

 

단축형 화살표 함수 표현식

let world4 = (name) => `hello ${name}`;

 

생성자 함수

되도록 사용하는 것을 권장하지 않음.

let world5 = new Function("name", 'return "hello" + name');

 

타입스크립트를 이용해서 함수 선언할 때 함수 생성자는 되도록 사용을 권장하지 않는 이유

 

타입 안전성 부족 : 

타입스크립트 컴파일러는 함수 생성자를 통해 생성된 코드의 타입을 미리 검사할 수 없음.
따라서, 런타임 오류가 발생할 가능성이 높아짐.


코드 가독성 저하 : 

함수 생성자는 함수의 본문을 문자열 형태로 작성하는데, 일반적인 함수 선언 방식에 비해 가독성이 떨어짐.
코드가 복잡해질수록, 문자열 안에 쓰인 로직을 이해하고 디버깅하는 것이 어려워질 수 있음.


타입스크립트의 주요 목적 중 하나는 코드의 안전성과 오류 최소화를 위해 정적 타입 체킹을 제공하는 것이지만
함수 생성자를 사용할 경우 이러한 장점을 이용하지 못하기 때문에 되도록 사용을 권장하지 않음.

 

https://ko.javascript.info/constructor-new

 

new 연산자와 생성자 함수

 

ko.javascript.info

 

TypeScript를 이용해 함수 사용하기

· TypeScript 함수 작성시 반환 타입을 추론하도록 하는 것을 권장한다.
· 함수의 매개변수인수의 타입이 호환 가능하게 작성한다.
· 인수의 타입을 잘못 전달하면 에러가 발생한다.

 

함수 선언식 (호이스팅 O)

function world(name: string): string{
	return `hello ${name}`;
}

 

함수 표현식 (호이스팅 X)

let world2 = function(name: string): string {
	return `hello ${name}`;
}

 

화살표 함수 표현식

let world3 = (name: string): string => {
	return `hello ${name}`;
}

 

단축형 화살표 함수 표현식

let world4 = (name: string): string => `
hello ${name}`;

 

타입추론

·  contextual typing  : TypeScript 컴파일러는 방정식의 한쪽에만 타입이 있더라도 타입을 추론할 수 있다.
·  이를 통해 프로그램에서 타입을 유지하기 위한 노력을 줄일 수 있다.

 

일반 함수

let f12 = function(x: number, y: number): number {
	return x + y;
}

 

contextual typing

 매개변수 x와 y는 number 타입

let f12: (baseValue: number increment: number) => number = function (x, y) {
	return x + y;
}

 

함수의 매개변수

기본 매개변수

·  함수에 주어진 인자의 수는 함수가 기대하는 매개변수의 수와 일치해야 한다.

function buildName(firstName: string, lastName: string) {
	return firstName + "" + lastName;
}

let result1 = buildName("Bob");  // error TS2554: Expected 2 arguments, but got 1.
let result2 = buildName("Bob", "Adams", "Sr.");  // error TS2554: Expected 2 arguments, but got 3.
let result3 = buildName("Bob", "Adams");

 

선택적 매개변수 (Optional Parameter)

·  JS에서는 모든 매개변수가 선택적으로, 인수가 없다면 undefined가 된다.

·  TS에서도 선택적 매개변수를 사용할 수 있다. (변수명 뒤에 '?')

·  선택적 매개변수는 필수 매개변수의에 와야 한다.

function buildName(firstName: string, lastName?: string) {
    if (lastName) return firstName + "" + lastName;
	else return firstName;
}

let result1 = buildName("Bob");  
let result2 = buildName("Bob", "Adams");
let result3 = buildName("Bob", "Adams", "Sr.");  // error TS2554: Expected 1-2 arguments, but got 3.

 

기본-초기화 매개변수 (Default Parameter)

· TS에서는 값을 제공하지 않거나, undefined로 했을 때에 매개변수의 값 할당 가능

function buildName(firstName: string, lastName = "Smith") {
    return firstName + "" + lastName;
}

let result1 = buildName("Bob");  // "Bob Smith"
let result2 = buildName("Bob", undefined);  // "Bob Smith"
let result3 = buildName("Bob", "Adams");  // "Bob Adams"
let result4 = buildName("Bob", "Adams", "Sr.");  // error TS2554: Expected 1-2 arguments, but got 3.

 

나머지 매개변수 (Rest Parameters)

· 컴파일러는 생략 부호(...) 뒤의 인자 배열을 빌드해 함수에서 사용할 수 있다.

· 나머지 매개변수는 매개변수의 수를 무한으로 취급한다.

· 아무것도 넘겨주지 않을 수 있다.

function buildName1 (firstName: string, ...restOfName: string[]) {
    // restOfName = [ 'Samuel', 'Lucas', 'MacKinzie' ]
    return firstName + " " + restOfName.join(" ");
}

let employeeName = buildName1("Joseph", "Samuel", "Lucas", "MacKinzie");
// Joseph Samuel Lucas MacKinzie

클래스

OOP(Object-Oriented Programming)

· OOP는 컴퓨터 프로그램을 객체(Object)의 모임으로 파악하려는 프로그래밍 패러다임이다.
· 객체(Object)들은 서로 메시지를 주고 받을 수 있으며, 데이터를 처리할 수 있다.

 

OOP의 장점
· 프로그램을 유연하고 변경을 용이하게 만든다.
· 프로그램의 개발과 보수를 간편하게 만든다.
· 직관적인 코드 분석을 가능하게 한다.
· 객체 지향 프로그래밍의 중요한 특성
· 강한 응집력(Strong Cohension)과 약한 결합력(Weak Coupling)을 지향한다.

- 응집력 : 서로 다른 모듈 간에 상호 의존하는 정도 또는 연관된 관계, 낮아야 좋음.

- 결합력 : 한 모듈 내부의 처리 요소들이 서로 관련되어 있는 정도(독립적 기능 수행), 높아야 좋음.

 

Class의 요소

멤버(member) 클래스에 속하는 모든 속성(필드)이나 메서드
필드(field) 클래스의 속성
해당 클래스의 인스턴스와 관련된 일부 특성이나 데이터
생성자(constructor) new 키워드를 사용하여 객체를 생성할 때 호출되는 메서드
메소드(method) 객체가 수행할 수 있는 동작이나 작업
클래스의 인스턴스에서 호출됨.

 

· 인스턴스(instance) : new 연산자에 의해서 생성된 객체

 

자바스크립트트

class Car {
  // Field(필드)
  brand;

  // Constructor(생성자)
  constructor(brand) {
    this.brand = brand;
  }

  // Method(메서드)
  startEngine() {
    console.log(`${this.brand}'s engine started.`);
  }
}

// 인스턴스 생성
const myCar = new Car("Toyota");
const anotherCar = new Car("Honda");

// 인스턴스 호출
myCar.startEngine();        // Toyota's engine started.
anotherCar.startEngine();   // Honda's engine started.

 

타입스크립트

class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    say() {
        return "Hello, My name is" + this.name;
    }
}

let person = new Person("june");

· new를 사용하여 Person 클래스의 인스턴스를 생성한다.

· Person class의 멤버는 name, constructor, say()가 있다.
· 클래스 안에서 "this."를 앞에 붙이면 클래스의 멤버를 의미한다.


접근 제어자 / 상속

접근 제어자
· 속성 또는 메소드로의 접근을 제한하기 위해 사용한다.
· TypeScript에는 3종류의 접근 제어자( public > protected > private )가 존재한다.
· Java와 다르게 package 개념이 없어서 default 접근 제어자는 존재하지 않는다.

 

접근 제어자(public)
· 프로그램 내에서 선언된 멤버들이 자유롭게 접근할 수 있다.
· TypeScript에서 멤버는 기본적으로 public으로 선언된다.
· 명시적으로 멤버를 public으로 표시할 수도 있다.

class Animal {
	public name: string
	constructor(theName: string) {
		this.name = theName;
	}
}

new Animal("Cat").name;

 

접근 제어자(private)
· 멤버가 포함된 클래스 외부에서의 접근을 막는다.

class Animal {
	private name: string
    constructor(theName: string) {
		this.name = theName;
	}
}

new Animal("Cat").name
// error TS2341: Property 'name' is private and only accessible within class 'Animal'.

 

접근 제어자(protected)

·  멤버가 포함된 클래스와 그 하위 클래스 외부에서의 접근을 막는다.

// 기본 클래스
class Animal {
  // protected 필드
  protected _name: string;

  constructor(name: string) {
    this._name = name;
  }

  // protected 메서드
  protected makeSound(): string {
    return "일부 일반적인 소리";
  }
}

// 파생 클래스 (Animal로부터 상속)
class Dog extends Animal {
  private _breed: string;

  constructor(name: string, breed: string) {
    // 기본 클래스의 생성자 호출
    super(name);
    this._breed = breed;
  }

  // 메서드 오버라이딩
  makeSound(): string {
    return "멍! 멍!";
  }

  // protected 필드와 메서드 사용하는 추가 메서드
  introduce(): string {
    return `나는 ${this._name}, ${this._breed} 강아지. ${this.makeSound()}`;
  }
}

// Dog 클래스의 인스턴스 생성
const myDog = new Dog("버디", "래브라도");

// 파생 클래스 내에서 protected 멤버에 접근
console.log(myDog.introduce()); 
// 출력: 나는 Buddy, 래브라도 강아지. 멍! 멍!

console.log(myDog._name);      
// 오류: Property '_name' is protected and only accessible within class 'Animal' and its subclasses.

 

상속

·  상속 : 상위 클래스의 기능을 재사용, 확장하는 문법

·  OOP는 상속을 이용하여 존재하는 클래스를 확장해 새로운 클래스를 생성할 수 있다.

·  하위클래스(subclass) : 파생된 클래스
    상위클래스(superclass) : 기초 클래스

// 기본 클래스
class Animal {
  private _name: string;

  constructor(name: string) {
    this._name = name;
  }

  // 이름을 반환하는 게터
  get name(): string {
    return this._name;
  }

  // 메서드
  makeSound(): string {
    return "일부 일반적인 소리";
  }
}

// 파생 클래스 (Animal로부터 상속)
class Dog extends Animal {
  private _breed: string;

  constructor(name: string, breed: string) {
    // 기본 클래스의 생성자 호출
    super(name);
    this._breed = breed;
  }

  // 메서드 오버라이딩
  makeSound(): string {
    return "멍! 멍!";
  }

  // 파생 클래스의 추가 메서드
  getBreed(): string {
    return this._breed;
  }
}

// 인스턴스 생성
const genericAnimal = new Animal("일반 동물");
const myDog = new Dog("버디", "래브라도");

// 기본 클래스의 메서드 사용
console.log(genericAnimal.name);         // 출력: 일반 동물
console.log(genericAnimal.makeSound());   // 출력: 일부 일반적인 소리

// 파생 클래스의 메서드 사용
console.log(myDog.name);                  // 출력: 버디
console.log(myDog.makeSound());            // 출력: 멍! 멍!
console.log(myDog.getBreed());             // 출력: 래브라도

Getters & Setters / readonly / static

Getters & Setters
· class의 속성에 직접 접근하는 것을 막고, getter, setter 함수를 사용해 값을 받아오거나 수정한다.
· 속성에 직접 접근해 수정하면 데이터 무결성이 깨질 수 있다. (캡슐화 권장)

· 비공개로 설정한 속성은 private로 설정하고, 속성값을 읽고 수정하는 getter/setter 함수를 사용한다

class Person {
	private _name: string

	get name() {
		return this._name;
	}

	set name(name: string) {
	if (name.length > 10) {
		throw new Error("name too long")	
	}
	this._name = name;
	}
}

let person = new Person();

console.log(person.name);
person.name = "june";

console.log(person.name);
person.name = "junejunejunejune";

 

readonly
· 속성을 읽기 전용으로 설정해 변경할 수 없게 만든다.
· 선언될 때나 생성자에서 값을 설정하면 이후 수정할 수 없다.

class Person {
	readonly age: number = 20  // 선언 초기화
	constructor(age: number) {
		this.age = age
	}
}

let person = new Person(10);  // 생성자 초기화
person.age = 30;

 

static

· 전역 멤버 : 객체마다 할당되지 않고 클래스의 모든 객체가 공유하는 멤버로, 범용적으로 사용되는 값에 설정한다.
· "클래스명."을 앞에 붙여 static 멤버에 접근할 수 있다
· ES6에서는 메소드 전용 속성에는 선언이 안 되었으나, TypeScript에서는 사용할 수 있다.

class MathUtility {
  static PI: number = 3.14159;
  static multiply(x: number, y: number): number {
    return x * y;
  }
}

// static 프로퍼티에 접근
console.log("PI:", MathUtility.PI);

// static 메소드를 호출
const result = MathUtility.multiply(5, 3);
console.log("Multiplication result:", result);

추상 클래스

·  추상 클래스 : abstract로 정의하며, 다른 클래스들이 파생될 수 있는 기초 클래스
·  추상 클래스는 직접 인스턴스를 생성할 수 없다.
·  추상 메소드는 클래스에는 구현되어 있지 않고, 파생된 클래스에서 구현해야 한다.

abstract class Shape {
  protected color: string;

  constructor(color: string) {
    this.color = color;
  }

  abstract calculateArea(): number;

  displayArea(): void {
    const area = this.calculateArea();
    console.log(`Color: ${this.color}, Area: ${area}`);
  }
}


class Circle extends Shape {
  radius: number;

  constructor(radius: number, color: string) {
    super(color); // 상위 클래스(Shape 클래스)의 생성자 호출
    this.radius = radius;
  }

  calculateArea(): number {
    return Math.PI * this.radius * this.radius;
  }
}

const shape = new Shape(8, "blue");  // 에러 : 추상클래스의 인스턴스는 생성 불가능
const circle = new Circle(5, "red");

circle.displayArea();

 

추상 클래스를 활용한 디자인 패턴(Template Method Pattern)
· 디자인 패턴 : 프로그램의 일부분을 서브 클래스로 캡슐화해 전체 구조를 바꾸지 않고, 특정 단계의 기능을 바꾸는 것
· 전체적인 알고리즘은 상위 클래스에서 구현, 다른 부분은 하위 클래스에서 구현한다.
· 전체 구조는 유사하지만 부분적으로 다른 구문으로 구성된 메소드의 코드 중복을 최소화할 수 있다.

 

728x90