Typescript에서 클래스 기능은 c#에서 유래된 것이 많다.
컴파일 후에 사라진다.
항상 대문자로 시작
class PersonContext {
name: string = "";
age: number = 0;
readonly location: string = "Korea"; // readonly 수정 불가능하게 한다.
초기화를 담당한다. -> 수정 가능하다.
constructor(name: string, age: number) { 인스턴스를 셋팅하고 초기화한다.
this.name = name // this가 생성될 인스턴스를 바라본다.
this.age = age
}
}
const p1 = new PersonContext(‘Jang’, 99); // 인스턴스
Const p2 = new PersonContext(‘Poco’, 100);
클래스에서 파생된 고유한 것 실제로 생성된 후 메모리에 올라간다.
설계도를 보고 실제로 벽돌을 쌓아 올린 실제 건물이다.
처음부터 끝까지 직접 코드를 짠다. 여러 개를 동시에 가져다 쓸 수 있다.
객체(클래스)에서는 행동을 뜻한다.
함수이기도 하다. 클래스가 가지고 있는 함수
introduce(): string {
return ${this.name}의 나이는 ${this.age}입니다.
}
console.log(p1.introduce())
필드의 접근할 권한을 가진 제어자이다. (외부에서 값이 어떻게 바뀌는지 통제 가능)
getter O / setter X => 속성은 자동으로 읽기 전용
setter 매개변수의 타입 X / getter의 반환 타입에서 추론
private 속성은 .연산자로 접근할 수 없다.
객체에 속성에 접근할 때 실행되는 함수이다.
get : 값을 읽을 때 실행
set : 값을 쓸 때 실행
class Person {
name: string;
private _age: number;
constructor(name: string, age: number) {
this.name = name
this._age = age
}
get age() {
if (this._age === 0) {
return '설정되지 않았습니다.'
}
return `나이는 ${this._age}세로 추정됩니다.`
}
set age(age) {
if (typeof age === 'number') {
this._age = age
}
this._age = 0;
}
const p = new Person('Jang' 99); // p가 인스턴스이다. 메모리 어딘가에 값을 담고 메서드를 실행할 수 있는 실체가 생겨난 것이다.
console.log(p.age)
console.log(p.name)
상속, 확장
class 기본 {
result() {
return 'Base'
}
}
class 파생 extends 기본 {
result() {
return 'Derived'
}
}
const de = new 파생()
console.log(de.result()) // "Drived"
super는 상속받은 부모 클래스를 의미해.
super() → 부모 클래스의 생성자 호출
super.method() → 부모 클래스의 메서드 호출
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
super(name); // ✅ 부모 생성자 호출 (필수)
this.breed = breed;
}
}
const dog = new Dog("Buddy", "Poodle");
public : 기본값, 어디서든 자유롭게 접근 가능
protected : 해당 클래스와 이를 상속받은 자식 클래스에서만 접근 가능하다.
private : 해당 클래스 내부에서만 접근 가능하다.
class Account {
public bankName: string = "Hana Bank"; // 누구나 접근 가능
protected owner: string; // 자신과 자식 클래스만 가능
private balance: number; // 오직 이 클래스 내부에서만
constructor(owner: string, initialBalance: number) {
this.owner = owner;
this.balance = initialBalance;
}
public showBalance() {
console.log(`잔액은 ${this.balance}원입니다.`); // 클래스 내부이므로 접근 가능
}
}
const myAccount = new Account("Stella", 10000);
console.log(myAccount.bankName); // OK
// console.log(myAccount.balance); // Error: private이라서 외부 접근 불가
인스턴스가 아닌, 클래스 자체에 귀속되는 속성이나 메서드이다.
인스턴스 생성하지 않고도 클래스 이름을 통해 바로 호출할 수 있다. 공유해야 하는 고정된 값이나 유틸리티 함수에 사용한다.
class Circle {
static PI: number = 3.14159; // 모든 원이 공유하는 고정값
static calculateArea(radius: number) {
return this.PI * radius * radius;
}
}
console.log(Circle.PI); // 인스턴스 없이 바로 접근
console.log(Circle.calculateArea(5));
값을 수정할 수 없게 만드는 제어자이다.
class Person {
public readonly birthDate: string;
constructor(date: string) {
this.birthDate = date; // 초기 할당 가능
}
updateBirth() {
// this.birthDate = "2000-01-01"; // Error: 읽기 전용 속성이라 수정 불가
}
}
abstract를 선언한 클래스로 직접 인스턴스를 생성 불가능하다.
직접 인스턴스화 될 수 없지만 extends후 파생된 클래스를 인스턴스화하도록 유도한다.
추상 클래스는 구현된 메서드를 포함시킬 수 있다.
abstract 선언한 메서드는 파생된 클래스에서 메서드를 구현해야 한다.
= 자식 클래스들이 공통으로 가져야 할 변수나 로직이 있다면 추상 클래스가 훨씬 유리하다.
전체적인 실행 흐름은 같은데, 특정 단계만 다를 때 사용한다.
abstract class GameCharacter {
constructor(protected hp: number) {}
// 모든 캐릭터가 공통으로 사용하는 로직
takeDamage(amount: number) {
this.hp -= amount;
console.log(`남은 체력: ${this.hp}`);
}
// 공격 방식은 캐릭터마다 다름
abstract attack(): void;
}
abstract class Animal {
// 선언된 메서드
abstract hello(str: string): string {
}
// 구현된 메서드
run() {
return this.hello() + 'run'
}
}
// abstract를 붙이면 직접 인스턴스화가 될 수 없다.
const animal = new Animal()
class Person extends Animal {
// 파생된 클래스는 가능하다.
hello() {
return 'Person'
}
}
const person = new Person()
console.log(person.hello()) // "Person"정상적으로 실행될 수 있다.
typescript에서 필드를 생성하고, 생성자 매개변수를 동일하게 만드는 과정이 귀찮을 때
접근제어자, 생성자 필드 네임, 타입을 생성자 매개변수를 받는 방법
= 매개변수에 다 넣고 this도 생략한다.
class Person {
constructor(public name: string, private age: number, protected gender: 'M' | 'F') { // 매개변수에 넣을 수 있다.
//this.name = name
//this.age = age
//this.gender = gender
= this도 생략이 가능하다.
}
sayName() {
return `이름은 ${this.name} 입니다.`
}
protected sayAge() {
return `나이는 ${this.age}`
}
}
부모 클래스에서 물려받은 메서드를 자신의 용도에 맞게 재정의 하는것이다.
클래스로 만든것을 확장하더라도 오버라이딩 할 수 있다.
1) extends 사용
class Animal {
run() {
return 'Animal이 달리다'
}
}
class Dog extends Animal {
run() {
return 'Dog이 달리다'
}
}
class Person extends Animal {
run() {
return 'Person이 달리다'
}
}
const p = new Person()
const d = new Dog()
console.log(p.run()) // Animal이 달리다
console.log(d.run())
2) super 키워드 활용
부모의 기능을 완전히 버리는 것이 아니라, 부모의 기능을 먼저 수행하고, 추가적인 로직을 더하고 싶을 때 super을 사용한다.
class Robot {
work() {
console.log("시스템 가동...");
}
}
class CleaningRobot extends Robot {
work() {
super.work(); // 부모의 work() 실행 ("시스템 가동...")
console.log("청소를 시작합니다."); // 자식만의 추가 로직
}
}
오버라이딩 : 상속 관계에서 부모의 메서드를 자신의 입맛에 맞게 재정의한다.
부모의 기능을 무시하거나(덮어쓰기)extends, super를 통해 부모의 기능을 포함하여 확장한다.
오버로딩 : 같은 이름의 메서드를 매개변수만 다르게 여러 방식으로 호출할 수 있게 하는 것이다.
(여러 옵션 쌓기), 매개 변수가 달라야 한다. 다양한 입력 타입에 유연하게 대응한다.
// 1. 오버로드 시그니처 (함수 선언부)
function add(a: string, b: string): string;
function add(a: number, b: number): number;
// 2. 함수 구현부 (실제 로직)
function add(a: any, b: any): any {
return a + b;
}
console.log(add(10, 20)); // 30 (number 타입으로 작동)
console.log(add("안녕", "세상")); // "안녕세상" (string 타입으로 작동)
// console.log(add(10, "안녕")); // Error: 선언부에 없는 조합은 사용 불가