TypeScript 클래스는 extends로 다른 클래스를 상속합니다. 하위 클래스는 부모의 모든 필드와 메서드를 받고, super(...)로 부모 생성자를 호출하며, 메서드를 재정의할 수 있고, TypeScript는 각 재정의가 부모의 타입과 호환되는지 검사합니다.
이것은 JavaScript의 클래스 상속입니다(그 뒤의 프로토타입 체인은 JavaScript의 상속을 보세요). TypeScript는 타입 검사를 더합니다. Dog는 Animal이 기대되는 어디에든 대입할 수 있고, 컴파일러가 재정의와 super 호출을 확인합니다.
메서드 재정의하기
같은 이름의 하위 클래스 메서드는 부모의 메서드를 대체합니다. 그 타입은 부모의 것에 대입 가능해야 합니다. 매개변수는 같거나 호환되고, 반환 타입은 같거나 더 좁아야 합니다. 그렇지 않으면 Animal을 가진 어떤 코드든 하위 클래스가 처리할 수 없는 인수로 메서드를 호출할 수 있습니다.
매개변수를 value: string으로 바꾸면 오류 TS2416: Property 'format' in type 'CurrencyFormatter' is not assignable to the same property in base type 'Formatter'입니다. 반복문이 이유를 보여 줍니다. 어떤 하위 클래스인지 모르는 채로 모든 요소에 숫자로 format을 호출합니다.
빈틈이 하나 있습니다. 메서드 매개변수는 이변적으로 비교되므로, format(value: 1 | 2)처럼 더 좁은 타입을 받는 재정의는 반복문이 여전히 1234567을 넘길 수 있는데도 컴파일됩니다. 반환 타입은 엄격하게 검사됩니다. string 대신 number를 반환하면 TS2416입니다.
override 키워드와 noImplicitOverride
override는 "이 멤버는 기반 클래스의 멤버를 대체한다"는 뜻이며, 컴파일러는 그 약속을 지키게 합니다. 기반에 그런 멤버가 없으면(오타나, 누군가 부모에서 이름을 바꾼 메서드) 오류입니다.
index.ts(9,14): error TS4117: This member cannot have an 'override' modifier because it is not declared in the base class 'Animal'. Did you mean 'speak'?
override가 없으면 그 오타는 컴파일되고 speak()는 조용히 "..."를 반환합니다. 키워드는 쓴 곳에서만 도움이 되므로 tsconfig.json에서 noImplicitOverride를 켜세요. 그러면 기반 멤버를 재정의하는 모든 메서드가 그렇다고 밝혀야 하며, 하위 클래스의 평범한 speak()는 오류 TS4114(This member must have an 'override' modifier because it overrides a member in the base class 'Animal')입니다.
{
"compilerOptions": {
"strict": true,
"noImplicitOverride": true
}
}
noImplicitOverride는 strict에 포함되지 않으므로 직접 추가해야 합니다. override는 컴파일된 JavaScript에서 지워집니다.
하위 클래스의 protected 멤버
부모의 private 멤버는 하위 클래스에서 보이지 않습니다. protected 멤버는 보이며, 그것이 protected가 존재하는 이유입니다. 부모 클래스가 자식에게 제공하는 API입니다.
private와 protected 모두 런타임에는 강제되지 않는 이유를 포함한 전체 규칙은 접근 제한자를 보세요.
더 좁은 타입으로 필드 다시 선언하기
하위 클래스는 필드에 부모가 말하는 것보다 더 구체적인 것이 들어 있다는 것을 아는 경우가 많습니다. 최신 클래스 필드에서 pet: Dog로 다시 선언하면 런타임 효과가 있습니다. super()가 반환된 뒤 하위 클래스 필드가 undefined로 초기화되어, 부모 생성자가 저장한 값을 지워 버립니다. 하위 클래스 생성자가 다시 대입하지 않는 한 TypeScript는 이를 TS2612(Property 'pet' will overwrite the base property in 'Home')로 보고합니다. 타입만 바꾸려면 declare를 쓰세요.
declare는 아무것도 출력하지 않으므로 Home의 생성자가 설정한 값이 살아남습니다. 더 좁은 타입을 참으로 만드는 것은 Dog만 받는 생성자입니다.
implements와 extends
두 키워드는 서로 다른 질문에 답하며, 클래스는 둘 다 쓸 수 있습니다.
extends | implements | |
|---|---|---|
| 대상 | 클래스 하나 | 인터페이스 몇 개든(또는 객체 타입의 타입 별칭) |
| 코드를 가져옴 | 예: 필드, 메서드, 생성자 | 아니요, 모양만 검사 |
| 런타임에 존재 | 예, 프로토타입 체인 | 아니요, 지워짐 |
instanceof 동작 | 예 | 아니요 |
| 대표적인 용도 | 실제 동작 공유 | 나머지 코드에 모양을 약속 |
클래스는 클래스 하나만 확장할 수 있습니다(TS1174: Classes can only extend a single class). 여러 곳의 동작을 조합하려면 인터페이스 여러 개를 구현하고, 합성(헬퍼 객체를 담는 필드)이나 믹스인으로 코드를 공유하세요.
믹스인
믹스인은 클래스를 받아서 그 하위 클래스를 반환하는 함수입니다. 타입 검사를 통과하는 일종의 다중 상속을 제공합니다.
Constructor의 any[]는 필수입니다. TypeScript는 생성자가 any[] 타입의 나머지 매개변수 하나만 받는 믹스인 기반만 허용합니다. 믹스인은 동작하지만 타입과 스택 트레이스를 읽기 어렵게 만들므로, 합성을 먼저 고려하세요.
흔한 실수
- 기반 생성자에서 재정의 가능한 메서드 호출하기. 기반 생성자는 하위 클래스의 필드가 초기화되기 전에 실행되므로,
this.items를 읽는 재정의는undefined를 보고,this.items.push(x)같은 호출은 런타임에TypeError를 던집니다. TypeScript는 이를 잡지 못합니다. super(...)를 잊거나 늦게 호출하기. 오류TS2377과TS17009입니다. 하위 클래스가this를 건드리기 전에 부모가 객체를 만들어야 합니다.declare없이 부모 필드 다시 선언하기. 하위 클래스 필드가 그 값을undefined로 초기화합니다.- 깊은 계층. 단계마다 자식이 부모의 내부에 묶입니다. 대개 두 단계면 충분하고, 그 이상이라면 합성이나 인터페이스를 쓰세요.
자주 묻는 질문
TypeScript에서 상속은 어떻게 동작하나요?
클래스는 extends로 다른 클래스를 상속합니다: class Dog extends Animal. 하위 클래스는 부모의 필드와 메서드를 받고, super(...)로 부모 생성자를 호출하며, 메서드를 재정의할 수 있습니다. TypeScript는 재정의가 부모와 타입 호환성을 유지하는지 검사합니다.
TypeScript에서 override 키워드는 무엇을 하나요?
override는 기반 클래스의 멤버를 대체하는 메서드나 속성을 표시합니다. 기반 클래스에 그 이름의 멤버가 없으면 컴파일러가 TS4117을 보고하므로, 오타나 부모에서 이름이 바뀐 멤버를 잡아냅니다. noImplicitOverride가 켜져 있으면 모든 재정의에 이 키워드가 있어야 합니다(그렇지 않으면 TS4114).
TypeScript에서 부모 클래스의 메서드는 어떻게 호출하나요?
하위 클래스 메서드 안에서 super.methodName(...)을 쓰세요. 생성자에서는 super(...)가 부모 생성자를 호출하며, this를 쓰기 전에 실행되어야 합니다.
TypeScript는 다중 상속을 지원하나요?
아니요. 클래스는 클래스 하나만 확장할 수 있습니다(TS1174: Classes can only extend a single class). 인터페이스는 몇 개든 구현할 수 있고, 믹스인(클래스를 받아 하위 클래스를 반환하는 함수)으로 여러 출처의 동작을 조합할 수 있습니다.
extends와 implements의 차이는 무엇인가요?
extends는 부모 클래스의 실제 코드를 상속합니다. implements는 클래스가 인터페이스의 모양에 맞는지만 검사하고 런타임에 아무것도 더하지 않습니다. 클래스는 둘 다 할 수 있습니다: class Dog extends Animal implements Pet.