→ 인터페이스는 변수의 타입으로 사용할 수 있다. 이때 인터페이스를 타입으로 선언한 변수는 해당 인터페이스를 준수해야 한다. 이것은 새로운 타입을 정의하는 것과 유사하다.
// 인터페이스의 정의
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 = (num: number) => {
return num * num;
}
console.log(squareFunc(10)); // 100
interface ITodo {
id: number;
content: string;
completed: boolean;
}
// Todo 클래스는 ITodo 인터페이스 양식을 구현하여야 한다
class Todo implements ITodo {
constructor (
public id: number;
public content: string;
public completed: boolean;
) { }
}
const todo = new Todo(1, 'Typescript', false);
console.log(todo);
interface IPerson {
name: string;
sayHello(): void;
}
/*
인터페이스를 구현하는 클래스는 인터페이스에서 정의한 프로퍼티와 추상 메서드를
반드시 구현해야함
*/
class Person implements IPerson {
constructor(public name: string) { }
sayHello() {
console.log(`Hello ${this.name}`);
}
}
function greeter(person: IPerson):void {
person.sayHello();
}
→ 주의해야 할 것은 인터페이스를 구현하였다는 것만이 타입 체크를 통과하는 유일한 방법은 아니다. 타입 체크에서 중요한 것은 실제로 값을 가지고 있다는 것이다. 예를 보자.
// 인터페이스 IDuck은 quack 메서드를 정의하였다.
interface IDuck { // 1
quack(): void;
}
// 클래스 MallardDuck은 인터페이스 IDuck을 구현하였다.
class MallardDuck implements IDuck { // 3
quack() {
console.log('Quack!');
}
}
// 클래스 RedheadDuck은 인터페이스 IDuck을 구현하지 않았지만 quack 메서드를 갖는다
class RedheadDuck { // 4
quack() {
console.log('q~uack!');
}
}
// makeNoise 함수는 인터페이스 IDuck을 구현한
// 클래스의 인스턴스 duck을 인자로 받는다.
function makeNoise(duck: IDuck): void { // 2
duck.quack();
}
makeNoise(new MallardDuck()); // Quack!
// makeNoise 함수에 인터페이스 IDuck을 구현하지 않은 **RedheadDuck의 인스턴스를 인자로 전달하여도 에러 없이 처리된다.**
makeNoise(new RedheadDuck()); // q~uack! // 5
⇒ TypeScript는 해당 인터페이스에서 정의한 프로퍼티나 메서드를 가지고 있다면 그 인터페이스를 구현한 것으로 인정한다. 이것을 덕 타이핑(duck typing) 또는 구조적 타이핑(structural typing)이라 한다.
?를 붙이며 생략하여도 에러가 발생하지 않는다.interface UserInfo {
username: string;
password: string;
age?: number;
address?: string;
}
const userInfo: UserInfo = {
username: 'taeill012@gmail.com',
password: 'Shintae13!'
}
console.log(userInfo);
→ 이렇게 선택적 프로퍼티를 사용하면 사용 가능한 프로퍼티를 파악할 수 있어서 코드를 이해하기 쉬워진다.
interface Person {
name: string;
age?: number;
}
interface Student extends Person {
grade: number;
}
const student: Student = {
name: 'Tommy',
age: 26,
grade: 3
}
복수의 인터페이스를 상속받을 수도 있다.
interface Person {
name: string;
age?: number;
}
interface Developer {
skills: string[];
}
interface WebDeveloper extends Person, Developer { }
const webDeveloper: WebDeveloper = {
name: 'Tommy',
age: 26,
skills: ['HTML', 'CSS', 'JavaScript', 'TypeScript']
}
인터페이스는 인터페이스 뿐만 아니라 클래스도 상속받을 수 있다. 단, 클래스의 모든 멤버 (public, protected, private)가 상속되지만 구현까지 상속받진 않는다.
class Person {
constructor(public name: string, public age: number) { }
}
interface Developer extends Person {
skills: string[];
}
const developer: Developer = {
name: 'Tommy',
age: 26,
skills: ['HTML', 'CSS', 'JavaScript', 'TypeScript']
}