인터페이스 (interface)
인터페이스는 클래스/객체가 갖는 구조를 표현하는 계약(Contract)으로서, 통상 인터페이스를 구현한 클래스는 인터페이스에 정의된 구조를 따라 멤버들을 정의한다. TypeScript에서 인터페이스는 interface 키워드를 사용하여 정의되며, 인터페이스 멤버로서 속성이나 메서드 원형을 가지게 된다. TypeScript의 interface 정의는 JavaScript로 변환되지는 않으며, 다만 TypeScript에서의 타입 체킹(type checking)을 위해 사용된다.
아래 예제는 속성과 메서드를 갖는 간단한 인터페이스이다. Order는 인터페이스 이름이고, id, product는 인터페이스가 갖는 속성이며, deliver()는 인터페이스가 갖는 메서드 원형을 정의한 것이다. 여기서 deliver()는 string 타입의 address를 입력파라미터로 받아들이고, boolean을 리턴하는 함수이다.
interface Order { id: number; product: string; deliver(address: string): boolean; }
인터페이스 : Type 정의
TypeScript에서 인터페이스는 객체의 Type을 정의하거나, 혹은 클래스에서 그 인터페이스를 구현할 때 사용될 수 있다. 먼저 객체의 타입을 정의하는 예로서, 아래는 2개의 속성을 갖는 타입을 정의한 인터페이스의 예이다.
interface Person { name: string; age: number; }
아래는 위 인터페이스를 사용하는 예제로서, TypeScript에서 변수를 인터페이스 타입으로 지정했을 경우에는 해당 변수는 인터페이스에 정의된 속성과 속성 타입에 부합하는 객체만을 할당할 수 있다. 예를 들어, 아래 코드에서 jack은 Person 타입에 정의된 속성을 모두 가지고 있어 컴파일이 성공적이지만, ken의 경우에는 address 라는 추가적인 속성이 있으므로 컴파일시 에러를 발생시킨다.
// jack은 Person 인터페이스 정의에 부합함 (OK) let jack: Person = {name: "Jack", age: 17}; // Person 인터페이스는 address 속성을 갖지 않음 (Error) let ken: Person = {name: "Ken", age: 15, address: "Seoul"};
아래 코드에서 checkMinor() 함수는 파라미터로 Person 인터페이스 타입을 받아들이고, 함수 안에서 name, age 등의 속성을 사용하고 있다. checkMinor(customer)와 같이 Person을 지정하지 않아도 코드는 동작하지만, 이 경우 인텔리센스와 TypeScript 고유의 타입 체킹을 사용할 수 없게 된다. 예제 하단에 함수를 호출하는 코드에서 변수 jack은 Person 인터페이스 타입으로 지정되어 있고 name과 age 속성을 가지고 있다. 반면, tom은 Person 인터페이스 타입으로 지정되어 있지 않으며, address 라는 추가적인 속성을 가지고 있다. checkMinor() 함수의 파라미터가 Person 인터페이스 타입을 요구하는데, tom은 이 인터페이스에 정의된 2개의 속성을 가지고 있으므로 입력 파라미터로 받아들여진다. 단, address 라는 추가적인 속성은 Person에 정의되어 있지 않으므로, checkMinor() 함수에서 사용될 수 없다.
function checkMinor(customer: Person) { if (customer.age < 18) { console.log(customer.name + " is a minor"); } else { console.log(customer.name + " is an adult"); } } // jack은 Person 인터페이스를 구현 (OK) let jack: Person = {name: "Jack", age: 17}; checkMinor(jack); // tom은 Person 인터페이스의 멤버들을 // 모두 가지고 있는 객체임 (OK) let tom = {name: "Tom", age: 18, address: "Seoul"}; checkMinor(tom);
인터페이스가 메서드 원형을 가지고 있는 경우, 해당 메서드 원형 (혹은 함수 타입)에 맞는 함수를 인터페이스 타입에 할당해서 사용할 수 있다. 아래 예제에서 ICalc 인터페이스는 하나의 메서드 원형을 정의하고 있는데, 즉 2개의 숫자를 입력받아 하나의 숫자를 리턴하는 메서드이다.
interface ICalc { (a: number, b: number): number; } function Add(a: number, b: number): number { return a + b; } function Substract(a: number, b: number): number { return a - b; } let calc: ICalc = Add; let c = calc(1, 2); // 1+2=3 console.log(c); calc = Substract; c = calc(c,2); //3-2=1 console.log(c);
인터페이스가 속성과 메서드를 모두 가지고 있는 경우, 아래 예제와 같이 인터페이스 타입의 변수는 정의된 속성을 지정할 뿐만 아니라, 메서드 원형에 맞는 메서드를 함께 지정하여 객체를 할당해야 한다.
interface Order { id: number; product: string; deliver(address: string): boolean; } function ship(address: string): boolean { console.log("Ship to " + address); return true; } let ord: Order = { id: 1, product:"CD", deliver:ship }; ord.deliver("Seoul");
인터페이스 : 클래스 구현
클래스는 인터페이스를 구현할 수 있는데, 이때 클래스는 인터페이스에 정의된 구조에 부합하도록 속성과 메서드를 구현해야 한다. 클래스는 인터페이스에 정의된 모든 속성과 메서드를 구현해야 하며, 추가적으로 다른 속성과 메서드를 정의하고 사용할 수 있다. TypeScript에서 클래스는 class 키워드를 써서 정의하고, 클래스명 뒤에 implements 키워드를 쓰고 다음에 인터페이스명을 적어 해당 클래스가 인터페이스를 구현한다는 것을 표시한다.
interface IOrder { id: number; product: string; deliver(address: string): boolean; } class Order implements IOrder { id: number; product: string; memo: string; // 추가적인 속성 constructor(id: number, product: string) { this.id = id; this.product = product; } deliver(address: string): boolean { console.log("Deliver " + this.product + " to " + address); return true; } // 추가적인 메서드 toString(): string { return this.id + "," + this.product + "," + this.memo; } } let ord = new Order(1, "CD"); ord.memo = "ASAP"; ord.deliver("Seoul"); console.log(ord.toString());