Menu

TypeScript 추상 클래스(abstract): 메서드, 속성, 인터페이스와 차이

TypeScript의 추상 클래스는 인스턴스를 만들 수 없고 메서드 구현을 하위 클래스에 맡길 수 있는 기반 클래스입니다. 추상 메서드와 추상 속성, 템플릿 메서드 패턴, 추상 생성자 타입, 인터페이스가 더 나은 선택인 경우를 알아봅니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

추상 클래스는 abstract로 표시된 기반 클래스입니다. new로 만들 수 없고, 본문 없는 abstract 메서드를 선언할 수 있으며, 모든 하위 클래스는 그것을 구현해야 합니다.

Shape는 모든 하위 클래스와 describe()를 공유하고, 각 하위 클래스가 area()를 제공하도록 강제합니다. Shape[] 타입은 하위 클래스를 어떻게 섞든 담습니다.

추상 클래스는 인스턴스를 만들 수 없다

추상 클래스에 new를 호출하면 컴파일 오류입니다. 만들어진 객체에 구현 없는 메서드가 있을 것이기 때문입니다.

index.ts(6,11): error TS2511: Cannot create an instance of an abstract class.

이것은 컴파일 시점의 규칙입니다. abstract는 출력에서 지워지므로 출력된 Shape는 평범한 JavaScript 클래스이고, 타입 없는 JavaScript 코드는 여전히 new Shape()를 호출할 수 있습니다. 라이브러리에 런타임 보호가 필요하다면 생성자가 new.target을 확인할 수 있습니다.

추상 메서드와 추상 속성

하위 클래스는 모든 추상 멤버를 구현하거나 자신도 추상이어야 합니다. 하나를 빠뜨리면 Non-abstract class 'Square' does not implement inherited abstract member area from class 'Shape' 같은 오류 TS2515가 나고, 여러 개를 빠뜨리면 그 모두를 나열하는 TS2654가 납니다.

필드, getter, 메서드 모두 추상일 수 있고, protected일 수도 있습니다.

추상 메서드에는 본문이 없습니다. 본문을 쓰면 오류 TS1245(Method 'x' cannot have an implementation because it is marked abstract)입니다. 추상 클래스가 추상 멤버를 반드시 가질 필요는 없습니다. abstract만 붙이면 직접 인스턴스를 만드는 것만 막습니다.

템플릿 메서드 패턴

추상 클래스를 쓰는 가장 흔한 이유입니다. 기반 클래스가 단계의 순서를 정하고, 각 하위 클래스가 달라지는 단계를 채웁니다.

여기처럼 추상 클래스도 제네릭일 수 있으므로, 각 하위 클래스가 레코드 타입을 고릅니다.

추상 생성자 타입

추상 클래스의 typeof Shape는 호출할 수 없는 생성자이므로, typeof Shape 타입의 매개변수로는 new c()를 할 수 없습니다. 함수가 추상 클래스를 받아야 한다면(예를 들어 등록하거나 instanceof를 확인하기 위해) 추상 생성 시그니처로 타입을 지정하세요.

abstract new는 추상 클래스와 구체 클래스를 모두 받고, 평범한 new는 구체 클래스만 받습니다.

추상 클래스와 인터페이스

추상 클래스인터페이스
코드를 담음예: 메서드, 필드 초기값, 생성자아니요, 타입만
런타임에 존재예아니요, 지워짐
instanceof동작함불가능
클래스가 쓸 수 있는 수하나(extends)여러 개(implements)
접근 제한자private, protected, publicpublic 멤버만
평범한 객체의 타입으로private/protected 멤버가 없을 때만, 그리고 그때 instanceof는 실패예, 모양이 맞는 모든 값

경험칙: 모양만 기술하면 될 때는 인터페이스를(가장 흔한 경우이며 객체 리터럴에도 쓸 수 있습니다), 하위 클래스들이 실제 구현을 공유하고 빈 부분을 채우도록 컴파일러가 강제하기를 원할 때는 추상 클래스를 쓰세요. 둘은 잘 어울립니다. 공개 계약에는 인터페이스를, 그것을 구현하는 편리한 기반 하나로 추상 클래스를 쓰면 됩니다. 또 다른 흔한 비교는 interface와 type을 보세요.

흔한 실수

  • 인터페이스로 충분한 곳에 추상 클래스 쓰기. 기반 클래스에 코드가 없다면, 런타임 무게와 단일 상속 제약이 더해진 인터페이스일 뿐입니다.
  • static abstract 시도하기. 허용되지 않습니다. 정적 멤버는 추상일 수 없습니다.
  • 추상 클래스에 new를 하면 런타임 오류가 날 거라 기대하기. new.target 검사를 직접 쓰지 않으면 오류는 없습니다.
  • 기반 생성자에서 추상 메서드 호출하기. 하위 클래스의 필드가 초기화되기 전에 실행되므로, 구현은 그 필드를 undefined로 봅니다.

자주 묻는 질문

TypeScript에서 추상 클래스란 무엇인가요?

abstract로 표시된 클래스로, new로 만들 수 없고 구현 없는 abstract 멤버를 선언할 수 있습니다. 하위 클래스는 모든 추상 멤버를 구현해야 합니다. 코드를 공유하면서 동시에 모양을 요구하는 방법입니다.

TypeScript에서 추상 클래스와 인터페이스의 차이는 무엇인가요?

추상 클래스는 실제 코드(구현된 메서드, 초기값이 있는 필드, 생성자)를 담을 수 있고 런타임에 존재하므로 instanceof가 동작하며, 클래스는 하나만 확장할 수 있습니다. 인터페이스는 코드가 없고 컴파일 시점에 지워지며, 클래스는 여러 개를 구현할 수 있습니다. 동작을 공유하려면 추상 클래스를, 모양을 기술하려면 인터페이스를 쓰세요.

TypeScript에서 추상 클래스가 생성자를 가질 수 있나요?

네. 하위 클래스가 super(...)를 호출할 때 실행됩니다. 여전히 추상 클래스 자체에 new를 호출할 수는 없습니다(TS2511: Cannot create an instance of an abstract class).

TypeScript에서 정적 메서드를 추상으로 만들 수 있나요?

아니요. abstract는 인스턴스 멤버에만 적용되며, static abstract는 오류 TS1243('static' modifier cannot be used with 'abstract' modifier)입니다. 정적 모양을 요구하려면 정적 멤버를 포함한 인터페이스로 생성자의 타입을 지정하고 클래스를 그것에 대해 검사하세요.

추상 클래스는 런타임에 강제되나요?

아니요. abstract는 지워지므로 컴파일된 클래스는 평범한 JavaScript이고, 평범한 JavaScript 코드는 여전히 new를 호출할 수 있습니다. 그것이 중요하다면 생성자에서 new.target을 확인하고 예외를 던지세요.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기