클래스 선언 뒤에 implements Printable을 붙이면, 이 클래스가 Printable 인터페이스의 모든 속성과 메서드 모양을 빠짐없이 갖춘다고 검사기에 약속하게 됩니다. 약속한 줄 가운데 하나라도 빠뜨리거나 타입이 다르면, 나중에 인스턴스를 만들어 넘기는 자리가 아니라 클래스를 선언한 바로 그 줄에서 TS2420 오류로 즉시 알려 줍니다.
도장 카드(StampCard)와 영수증(Receipt)을 같은 출력 규격(Printable)에 맞추어 보면서, implements로 모양을 점검하는 법과 extends 상속과의 차이, 그리고 구조적 타이핑(검사 규칙 4)이 클래스와 어떻게 이어지는지 살펴봅니다.
1implements로 클래스 선언 자리에서 모양 점검하기
출력기에 넘길 수 있는 대상이라면 모두 owner: string, stamps: number, format(): string 세 가지를 갖춰야 한다고 합시다. 먼저 interface Printable로 이 규격을 적고, StampCard에 implements Printable을 붙입니다.
interface Printable {
owner: string;
stamps: number;
format(): string;
}
class StampCard
implements Printable
{
owner: string;
stamps: number = 0;
constructor(owner: string) {
this.owner = owner;
}
format(): string {
return (
`${this.owner}:` +
`${this.stamps}`
);
}
}
이제 Printable을 받는 함수 printItem에 StampCard 인스턴스를 넘길 수 있습니다.
function printItem(
item: Printable,
): void {
console.log(item.format());
}
const card =
new StampCard("민지");
printItem(card);
민지:0만약 StampCard를 작성하다가 format 메서드를 깜빡 빠뜨리면 어떻게 될까요?
interface Printable {
owner: string;
stamps: number;
format: () => string;
}
class StampCard
implements Printable
{
owner: string;
stamps: number = 0;
constructor(owner: string) {
this.owner = owner;
}
}
main.ts(7,7): error TS2420: Class 'StampCard' incorrectly implements interface 'Printable'. Property 'format' is missing in type 'StampCard' but required in type 'Printable'.printItem(card)를 아직 한 번도 부르지 않았는데도, 7번 줄 class StampCard 선언 자리에서 format이 빠졌다고 바로 짚어 줍니다.
2implements와 extends의 차이
두 키워드는 이름이 비슷해 보이지만 하는 일이 완전히 다릅니다.
implements 인터페이스: 모양 검사만 합니다. 물려받는 실행 코드는 한 줄도 없고, 타입 지우기 단계에서implements Printable통째로 사라집니다. 한 클래스가implements Printable, Storable처럼 여러 인터페이스를 동시에 지킬 수도 있습니다.extends 부모클래스: 부모 클래스의 실제 속성과 메서드를 물려받는 자바스크립트 상속 문법입니다. 타입을 지운 뒤에도 그대로 남아 실행됩니다.
class BaseCard {
owner: string;
constructor(owner: string) {
this.owner = owner;
}
}
class VipCard extends BaseCard {
tier: string = "VIP";
}
const vip = new VipCard("민지");
console.log(vip.owner);
console.log(vip.tier);
민지
VIP예제
실행 결과를 먼저 적어 본 뒤 펼쳐 보세요. 맞혀 보기는 점수에 들지 않습니다.
Printable 인터페이스를 지키는 Receipt 클래스를 만들고 format()을 찍습니다.
interface Printable {
title: string;
format(): string;
}
class Receipt
implements Printable
{
title: string;
constructor(title: string) {
this.title = title;
}
format(): string {
return `[${this.title}]`;
}
}
const r = new Receipt("라테");
console.log(r.format());
무엇이 찍힐까요?
실행 결과 보기
[라테]Receipt는 Printable이 요구한 title 속성과 format() 메서드를 모두 갖추었으므로 implements Printable 검사를 통과하고 [라테]를 출력합니다.
자주 틀리는 곳
틀린 생각: implements를 붙이면 클래스 메서드의 매개변수 타입도 인터페이스에서 알아서 추론해 준다.
interface CounterRule에 add(n: number): number;라고 적어 두었으니, implements CounterRule을 붙인 클래스 안에서는 add(n)처럼 매개변수 타입 : number를 생략해도 알아서 number로 채워 줄 것이라고 기대하기 쉽습니다(implements가 매개변수 타입을 추론한다는 생각).
interface CounterRule {
add(n: number): number;
}
class StampCard
implements CounterRule
{
stamps: number = 0;
add(n): number {
this.stamps += n;
return this.stamps;
}
}
main.ts(9,7): error TS7006: Parameter 'n' implicitly has an 'any' type.맞는 규칙: implements는 클래스가 완성된 뒤 모양이 맞는지 대조만 할 뿐, 메서드 매개변수 타입을 거꾸로 채워 주지 않습니다. implements를 썼더라도 클래스 메서드의 매개변수 타입(add(n: number))은 반드시 직접 적습니다.
틀린 생각: implements를 적지 않은 클래스나 객체는 interface 자리에 넘길 수 없다.
타입스크립트는 구조적 타이핑(검사 규칙 4)을 따릅니다(이름이 같아야만 들어간다는 생각).
interface Printable {
owner: string;
format(): string;
}
function show(p: Printable) {
console.log(p.format());
}
const guest = {
owner: "준호",
format() {
return `손님:${this.owner}`;
},
};
show(guest);
손님:준호맞는 규칙: implements Printable을 적지 않은 클래스나 일반 객체라도 owner, format 모양만 맞으면 Printable 자리에 들어갑니다. 그럼에도 클래스에 implements Printable을 적는 까닭은, 클래스를 고치다가 속성을 빠뜨렸을 때 클래스 선언 자리에서 바로 오류를 잡기 위해서입니다.
확인 문제
1. 구조적 타이핑 덕분에 모양만 맞으면 implements Printable 없이도 Printable 자리에 들어갈 수 있는데, 왜 클래스에 implements Printable을 적을까요?
힌트
1절에서 format 메서드를 빠뜨렸을 때 어느 줄에서 오류가 났는지 떠올려 보세요.
정답
속성이나 메서드를 빠뜨렸을 때 클래스 선언 자리에서 즉시 TS2420 오류를 보기 위해서입니다. implements를 안 적어 두면 나중에 그 인스턴스를 Printable 자리에 실제로 넘기는 코드를 쓸 때까지 무엇이 빠졌는지 알기 어렵습니다.
2. interface에 add(n: number): number가 적혀 있을 때, 이를 implements한 클래스에서 add(n)처럼 매개변수 타입을 빼면 어떻게 되나요?
힌트
implements가 매개변수 타입을 추론해 주는지 생각해 보세요.
정답
매개변수 n이 암묵적 any가 되어 TS7006 오류가 납니다. implements는 클래스의 모양이 인터페이스에 맞는지 확인할 뿐, 메서드 매개변수 타입을 인터페이스에서 가져와 채워 주지 않습니다(implements가 매개변수 타입을 추론한다는 생각).
3. implements와 extends 가운데 타입을 지운 뒤 자바스크립트 실행 코드에 남는 것은 어느 쪽인가요?
힌트
2절에서 두 키워드의 역할을 비교한 부분을 살펴보세요.
정답
extends만 남습니다. extends는 부모 클래스를 실제로 상속하는 자바스크립트 문법이고, implements는 타입 검사 전용 표기라 실행 전에 완전히 지워집니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
1절의 StampCard 코드에서 format 메서드를 지우고 npx tsc를 돌려 7번 줄에서 TS2420이 나는지 확인해 보세요. 다음은 단원 연습 문제에서 9단원 전체 내용을 문제로 점검합니다.
검사하고 돌리는 법은 내 컴퓨터에서 검사하고 돌리기에 있습니다.