[TypeScript] 기초 - 인터페이스(Interface)
인터페이스
소개
인터페이스는 일반적으로 타입 체크를 위해 사용되며 변수, 함수, 클래스에 사용할 수 있습니다. 인터페이스는 여러가지 타입을 갖는 프로퍼티로 이루어진 새로운 타입을 정의하는 것과 유사합니다. 인터페이스에 선언된 프로퍼티 또는 메소드의 구현을 강제하여 일관성을 유지할 수 있도록 하는 것입니다. ES6는 인터페이스를 지원하지 않지만 Typescript에서는 지원합니다.
인터페이스는 프로퍼티와 메서드를 가질 수 있다는 점에서 클래스와 비슷하나 직접 인스턴스를 생성할 수 없고 모든 메서드는 추상 메서드입니다.
변수와 인터페이스
인터페이스는 변수의 타입으로 사용할 수 있습니다. 이때 인터페이스를 타입으로 선언한 변수는 해당 인터페이스를 준수하여야 하는데 이 부분이 새로운 타입을 정의하는 것과 유사합니다.
// 인터페이스의 정의
interface Todo {
id: number;
content: string;
completed: boolean;
}
// 변수 todo의 타입으로 Todo 인터페이스를 선언하였다.
let todo: Todo;
// 변수 todo는 Todo 인터페이스를 준수하여야 한다.
todo = { id: 1, content: 'typescript', completed: false };
인터페이스를 사용하여 함수 파라미터의 타입도 선언할 수 있습니다. 이때 해당 함수에는 함수 파라미터의 타입으로 지정한 인터페이스를 준수하는 인수를 전달하여야 합니다. 이렇게 사용하면 함수에 객체를 전달할 때 복잡한 매개변수 체크가 필요없어서 매우 유용하다고 합니다.
// 인터페이스의 정의
interface Todo {
id: number;
content: string;
completed: boolean;
}
let todos: Todo[] = [];
// 파라미터 todo의 타입으로 Todo 인터페이스를 선언하였다.
function addTodo(todo: Todo) {
todos = [...todos, todo];
}
// 파라미터 todo는 Todo 인터페이스를 준수하여야 한다.
const newTodo: Todo = { id: 1, content: 'typescript', completed: false };
addTodo(newTodo);
console.log(todos)
// [ { id: 1, content: 'typescript', completed: false } ]
함수와 인터페이스
함수의 타입으로도 사용할 수 있습니다. 이때 함수의 인터페이스에는 타입이 선언된 파라미터 리스트와 리턴 타입을 정의합니다. 함수 인터페이스를 구현하는 함수는 마찬가지로 인터페이스를 준수하여야 합니다.
// 함수 인터페이스의 정의
interface SquareFunc {
(num: number): number;
}
// 함수 인테페이스를 구현하는 함수는 인터페이스를 준수하여야 한다.
const squareFunc: SquareFunc = function (num: number) {
return num * num;
}
console.log(squareFunc(10)); // 100
클래스와 인터페이스
클래스 선언문의 implements
뒤에 인터페이스를 선언하면 해당 클래스는 지정된 인터페이스를 반드시 구현하여야 합니다. 이는 인터페이스를 구현하는 클래스의 일관성을 유지할 수 있는 장점이 있습니다. 인터페이스는 클래스와 유사하나 직접 인스턴스를 생성할 수 없습니다.
// 인터페이스의 정의
interface IPerson {
name: string;
sayHello(): void;
}
/*
인터페이스를 구현하는 클래스는 인터페이스에서 정의한 프로퍼티와 추상 메소드를 반드시 구현하여야 한다.
*/
class Person implements IPerson {
// 인터페이스에서 정의한 프로퍼티의 구현
constructor(public name: string) {}
// 인터페이스에서 정의한 추상 메소드의 구현
sayHello() {
console.log(`Hello ${this.name}`);
}
}
const me = new Person('Lee');
greeter(me); // Hello Lee
덕 타이핑(Duck typing)
인터페이스를 구현했다는 것만으로 타입 체크를 통과하는 유일한 방법은 아닙니다. 타입 체크에서 중요한 것은 값을 실제로 가지고 있어야합니다.
interface IDuck { // 1
quack(): void;
}
class MallardDuck implements IDuck { // 3
quack() {
console.log('Quack!');
}
}
class RedheadDuck { // 4
quack() {
console.log('q~uack!');
}
}
function makeNoise(duck: IDuck): void { // 2
duck.quack();
}
makeNoise(new MallardDuck()); // Quack!
makeNoise(new RedheadDuck()); // q~uack! // 5
- 인터페이스 IDuck은 quack 메서드를 정의하였습니다.
- makeNoise 함수는 인터페이스 IDuck으로 구현한 클래스의 인스턴스 duck을 인자로 전달받습니다.
- 클래스 MallardDuck은 인터페이스 IDuck을 구현했습니다.
- 클래스 ReadheadDuck은 인터페이스 IDuck을 구현하지는 않았지만, quack 메서드를 갖습니다.
- makeNoise 함수에 인터페이스 IDuck을 구현하지 않은 클래스 ReadgeadDuck의 인스턴스를 인자로 전달하여도 에러 없이 처리됩니다.
타입스크립트는 인터페이스에서 정의한 프로퍼티나 메서드를 가지고 있다면 그 인테페이스를 구현한 것으로 인정합니다. 이것을 덕 타이핑 혹은 구조적 타이핑이라고 합니다. 이는 변수에 사용할 경우에도 적용됩니다.
선택적 프로퍼티
인터페이스의 프로퍼티는 반드시 구현되어야 합니다. 하지만 인터페이스의 프로퍼티가 선택적으로 필요한 경우가 있는데 이러한 경우에는 프로퍼티명 뒤에 ?
를 붙이면 생략하여도 에러가 발생하지 않습니다. 이러한 것은 선택적 프로퍼티라고 합니다.
interface UserInfo {
username: string;
password: string;
age? : number;
address?: string;
}
const userInfo: UserInfo = {
username: 'ungmo2@gmail.com',
password: '123456'
}
console.log(userInfo);
읽기전용 프로퍼티(Readonly)
일부 프로퍼티들은 객체가 청므 성생될 때만 수정 가능해야합니다. 프로퍼티 이름 앞에 readonly
를 넣어서 이를 지정할 수 있습니다.
interface Point{
readonly x: number;
readonly y: number;
}
let p1: Point = {x:10, y:20};
p1.x = 5 //오류
readonly vs const
readonly와 const는 비슷하게 동작을 하는데 변수는 const, 프로퍼티는 readonly를 사용합니다.
인터페이스 상속
인터페이스는 extends 키워드를 사용해 인터페이스 또는 클래스를 상속받을 수 있고 복수의 인터페이스를 상속받을 수 있습니다.
interface Person {
name: string;
age?: number;
}
interface Developer {
skills: string[];
}
interface WebDeveloper extends Person, Developer {}
const webDeveloper: WebDeveloper = {
name: 'Lee',
age: 20,
skills: ['HTML', 'CSS', 'JavaScript']
}
익명 인터페이스
interface 키워드를 사용하지 않고 interface를 선언할 수 있는데 이를 익명 인터페이스라고 합니다. 익명 인터페이스는 다음과 같이 사용할 수 있습니다.
let ai: {
name: string,
age: number,
etc?: boolean
} = {name: 'Jack', age: 32}
익명 인터페이스는 주로 아래처럼 함수를 구현할 때 많이 사용한다고 합니다.
function printMe(me: {name: string, age: number, etc?: boolean}) {
console.log(me};
}