본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로프로그래밍 › TypeScript › 언어 기능

TypeScript 클래스(Class)

목차

TypeScript 클래스(Class)

TypeScript의 클래스는 JavaScript class 문법에 타입 검사를 더한다. 아래 예시는 메서드, 필드, 읽기 전용 속성, 인터페이스 구현을 차례로 살펴본다. 타입 표기는 컴파일 후 JavaScript 실행 시에는 사라진다.

생성자와 메서드

class Greeter {
  constructor(message: string) {
    console.log(`construct! ${message}`);
  }

  greet(name: string): void {
    console.log(`${name}, Hello TypeScript!`);
  }
}
new Greeter('hhh').greet('홍길동');

constructor는 new로 인스턴스를 만들 때 실행된다. greet는 메서드이고, 매개변수 name은 문자열이어야 한다.

클래스 필드와 초기화

타입 검사를 켜면 사용하려는 필드는 클래스 안에 선언하고, 초기값 또는 생성자에서 값을 할당한다.

class FieldTrip {
  destination: string;

  constructor(destination: string) {
    this.destination = destination;
  }
}
const trip = new FieldTrip('Seoul');
console.log(trip.destination);

다음처럼 선언할 때 바로 초기화할 수도 있다. next()가 배열을 새로 만들어 남은 항목을 보관하므로 호출 전후의 순서를 이해하기 쉽다.

class ActivitiesQueue {
  private pending: string[] = [];

  init(items: string[]): void {
    this.pending = [...items];
  }

  next(): string | undefined {
    const [item, ...rest] = this.pending;
    this.pending = rest;
    return item;
  }
}
const queue = new ActivitiesQueue();
queue.init(['eat', 'sleep', 'learn']);
console.log(queue.next()); // eat

원래 예제의 pending!: string[]에 붙은 !는 확정 할당 단언(definite assignment assertion)이다. 생성자 밖에서 반드시 초기화하겠다고 타입 검사기에 알릴 뿐, 배열을 만들어 주지 않는다. init() 전에 pending.pop()을 호출하면 런타임 오류가 날 수 있다. 확실한 이유가 없으면 위처럼 초기값을 주는 편이 안전하다. 자세한 조건은 TypeScript 클래스 문서에 설명돼 있다.

메서드와 함수 값 필드

class WithMethod {
  myMethod(): void {
    console.log('method');
  }

  taskParams = (input: boolean): string => input ? 'YES' : 'NO';
}
const instance = new WithMethod();
console.log(instance.taskParams(true));

myMethod는 프로토타입 메서드다. taskParams는 인스턴스를 만들 때 생성되는 함수 값 필드다. 화살표 함수 필드는 this를 인스턴스에 묶어 쓰기 편하지만, 인스턴스마다 함수가 만들어진다는 차이가 있다.

readonly와 인터페이스

class Quote {
  readonly text: string;

  constructor(text: string) {
    this.text = text;
  }
}
const quote = new Quote('Hello');
console.log(quote.text);
// quote.text = 'Bye'; // 타입 오류: 생성자 밖에서 재할당 불가

interface Learner {
  name: string;
  study(hours: number): void;
}
class Student implements Learner {
  constructor(public name: string) {}

  study(hours: number): void {
    console.log(`${this.name}: ${hours}시간 학습`);
  }
}
new Student('홍길동').study(2);

readonly는 필드에 다시 값을 할당하는 것을 타입 검사에서 막는다. 객체 전체를 깊게 불변으로 만들거나 런타임 변경을 자동으로 막지 않는다. implements는 클래스가 인터페이스에서 요구한 필드와 메서드를 갖는지 검사한다. constructor(public name: string)은 매개변수와 인스턴스 필드를 함께 선언하는 TypeScript 문법이다. 공식 클래스 가이드는 이 동작과 사용 범위를 설명한다.

접근 제한은 타입 검사와 실행을 나누어 보기

TypeScript의 private는 일반적으로 컴파일 시 접근 검사다. JavaScript로 변환된 뒤에도 언어 차원에서 접근을 막는 #privateField와는 다르다. 외부 JavaScript 코드나 타입 단언으로 접근 제한을 우회할 수 있으므로 비밀 값의 보안 경계로만 믿지 않는다.

class Account {
  #balance: number;
  constructor(initial: number) {
    if (!Number.isFinite(initial) || initial < 0) throw new RangeError("초기 잔액");
    this.#balance = initial;
  }
  balance(): number { return this.#balance; }
  deposit(amount: number): void {
    if (!Number.isFinite(amount) || amount <= 0) throw new RangeError("입금액");
    this.#balance += amount;
  }
}
const account = new Account(10);
account.deposit(5);
console.log(account.balance()); // 15
// account.#balance; // 클래스 외부에서는 문법 오류

#balance는 JavaScript의 비공개 필드다. 타입 선언뿐 아니라 실행 중 접근도 제한한다. 위 검증은 음수·NaN을 막기 위한 예시이며 실제 금액이라면 부동소수점의 정밀도도 고려해야 한다. 단위를 정수 최소 화폐 단위 또는 별도 금액 타입으로 표현할 수 있다. 공개 API가 클래스의 내부 필드를 직접 만지게 하기보다 메서드로 허용된 상태 변경을 표현하면 유지보수가 쉽다.

참고: TypeScript Classes, MDN Private elements

같은 카테고리의 글