프론트엔드 스터디/Typescript

[TypeScript] 기초 - 인터페이스(Interface)

옹재 2021. 7. 20. 16:41
728x90
반응형

인터페이스

소개

인터페이스는 일반적으로 타입 체크를 위해 사용되며 변수, 함수, 클래스에 사용할 수 있습니다. 인터페이스는 여러가지 타입을 갖는 프로퍼티로 이루어진 새로운 타입을 정의하는 것과 유사합니다. 인터페이스에 선언된 프로퍼티 또는 메소드의 구현을 강제하여 일관성을 유지할 수 있도록 하는 것입니다. 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
  1. 인터페이스 IDuck은 quack 메서드를 정의하였습니다.
  2. makeNoise 함수는 인터페이스 IDuck으로 구현한 클래스의 인스턴스 duck을 인자로 전달받습니다.
  3. 클래스 MallardDuck은 인터페이스 IDuck을 구현했습니다.
  4. 클래스 ReadheadDuck은 인터페이스 IDuck을 구현하지는 않았지만, quack 메서드를 갖습니다.
  5. 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};
    }
728x90
반응형