인터페이스란?

인터페이스는 상호 간에 정의한 약속 혹은 규칙을 의미한다. 인터페이스는 보통 다음과 같은 범주에 대해서 약속을 정의할 수 있다.

  • 객체의 스펙(속성과 속성의 타입)
  • 함수의 파라미터
  • 함수의 스펙(파라미터, 반환 타입 등)
  • 배열과 객체를 접근하는 방식
  • 클래스

인터페이스 예제

let user = { name: 'seongsik', age: 20 };

function who( obj: { name: string, age: number }) {
  console.log(obj.name); // seongsik
  console.log(obj.age); // 20
}

who(user);

위의 who 함수는 name과 age 속성을 갖는 객체를 인자로 받는다. 이렇게 인자를 받을 때 단순히 타입 뿐만 아니라 객체의 속성 타입까지 정의할 수 있다. 아래와 같이 인터페이스를 적용해보자.

interface whoType {
  name: string;
  age: number;
}


let user = { name: 'seongsik', age: 20 };

function who( obj: whoType) {
  console.log(obj.name);
  console.log(obj.age);
}

who(user);

인터페이스를 사용하면 코드를 명시적으로 바뀔 수 있다. 이제 who() 함수는 whoType라는 타입만 가져야 한다.


인터페이스 옵션 속성

인터페이스에 명시되어 있는 속성타입을 모두 다 사용할 필요는 없다. 이를 가능하게 하는 것이 옵션 속성이다.

아래의 예제 코드를 보자.

interface 인터페이스이름 {
  속성1: string;
  속성2: number;
  속성3?: number;
}

이처럼 ? 물음표를 속성 끝에 붙이면 해당 속성을 꼭 사용하지 않아도 된다.

interface whoType {
  name: string;
  age: number;
  address?: string;
}

function who( obj: whoType) {
  console.log(obj.name);
  console.log(obj.age);
}

let user = { name: 'seongsik', age: 20 };

who(user);

인자로 넘긴 user객체에 address 속성이 없어도 에러가 발생하지 않는다. whoType타입에 address 인자에 ? 붙여 옵션 속성을 사용했기 때문이다.


인터페이스 읽기 전용 속성

읽기 전용 속성은 인터페이스로 객체를 처음 생성할 때 값을 할당하고, 그 이후에는 변경할 수 없는 속성을 말한다.

아래의 코드와 같이 readonly 속성을 사용하면 오직 읽기만 가능하기 때문에 선언이후 수정하려고 하면 아래와 같이 오류가 발생한다.

interface userType {
 readonly name: string;
}

let user: userType = {
  name: 'seongsik'
};

user.name = 'siksik'; // 읽기 전용 속성이므로 'name'에 할당할 수 없습니다.


인터페이스 읽기 전용 배열

배열의 읽기전용 속성은 ReadonlyArray<T> 타입속성을 사용하면 읽기 전용 배열을 생성할 수 있다.

만약 배열을 수정하려고 하면 아래와 같이 오류가 발생한다.

let arr: ReadonlyArray<number> = [1,2,3,4];

arr[0]=100; // 'readonly number[]' 형식의 인덱스 시그니처는 읽기만 허용됩니다.
arr.push(5); // 'readonly number[]' 형식에 'push' 속성이 없습니다.


객체 선언과 관련된 타입 체킹

타입스크립트는 인터페이스를 통해 객체를 선언할 때 좀더 엄밀한 속성 검사를 진행한다.

interface userTpye {
  name?: string;
}


function user(user: userTpye) {
  console.log(user);
}

user({nam:'seongsik'}) // '{ nam: string; }' 형식의 인수는 'userTpye' 형식의 매개 변수에 할당될 수 없습니다. 개체 리터럴은 알려진 속성만 지정할 수 있지만 'userTpye' 형식에 'nam'이(가) 없습니다. 'name'을(를) 쓰려고 했습니까?

인터페이스에서는 name으로 선언했지만 함수에 인자로 넘기는 객체는 nam로 선언되어 있기 때문에 오류가 발생한다.

만약 이런 타입 추론을 무시하고 싶으면 아래와 같이 선언한다.

let myName = {nam:'seongsik'}
user(myName as userTpye)

또한, 인터페이스에 정의하지 않는 속성들을 추가로 사용하고 싶다면 아래와 같이 작성한다.

interface userTpye {
  name?: string;
  [propName: string]: any;
}


인터페이스 함수 타입

인터페이스는 함수의 타입도 정의할 수 있다.

interface fnType {
  (name:string, age:string): string;
}
let fn: fnType
fn = (name:string, age:string) => {
  console.log(name); // seongsik
  console.log(age);  // 20
  return `이름은 ${name}, 나이는 ${age}`;
}

console.log(fn('seongsik', '20')); // 이름은 seongsik, 나이는 20


인터페이스 클래스 타입

자바와 같은 클래스도 타입 규칙을 적용할 수 있다.

interface userTpye {
  name: string;
  fnUser(user: string): void;
}


class User implements userTpye {
  name : string = 'seongsik';
  fnUser(user: string){
    this.name = user;
  }
  constructor() {}
}


let test = new User;
console.log(test.name) // seongsik
test.fnUser('siksik') 
console.log(test.name) // siksik


인터페이스 확장

인터페이스도 클래스처럼 확장할 수 있다.

interface User {
  name: string;
}

interface Job extends User {
  position: string;
}

let Who : Job = {
  name: 'seongsik',
  position: 'developer'
}

// 혹은

let who2 = {} as Job;
who2.name = 'seongsik';
who2.position = 'developer';


인터페이스 하이브리드 타입

자바스크립트의 유연하고 동적인 타입 특성을 고려해 인터페이스도 여러 가지 타입을 조합해 사용할 수 있도록 되어있다. 예를 들어, 함수 타입이면서 객체 타입을 정의할 수 있는 인터페이가 있다.

interface UserType {
  (name: string): string;
  position: string;
  brew(): void;
}

function User(): UserType {
  let who = (function(name: string) {}) as UserType;
  who.position = 'developer';
  who.brew = function() {};
  return who;
}

let Who = User();
Who('seongsik');
Who.position = 'HR';
Who.brew();

console.log(Who);