본문 바로가기
프로그래밍/typescsript

타입스크립트의 인터페이스

by 한코코 2022. 11. 21.

인터페이스란?

인터페이스는 상호 간에 정의한 약속이나 규칙을 뜻하며, 타입스크립트에서의 인터페이스는 보통 다음과 같은 범주에 대해 약속을 정의할 수 있다.

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

 

 

 

 

인터페이스로 함수 정의하기

// 인터페이스로 함수 정의하기
interface Add{
  // (num1:number, num2:number): void; -> return 값이 없을때
  (num1:number, num2:number): number;
}

const add : Add = function(x,y){
  return x+y;
}

add(10,20);
interface IsAdult {
 (age:number) : boolean; 
}

const a: IsAdult = (age) => {
  return age>19;
}

a(33);

 

 

 

 

인터페이스로 클래스 정의하기

// implements -> 구현하다(구글번역)
interface Car { 
  color: string;
  wheels: number;
  start(): void;
}

// Car의 타입들을 가져와서 BMW에 적용한다는 의미
class BMW implements Car {
  color;
  wheels = 4;
}

이렇게만 작성하면 class BMW는 에러를 띄운다.

 

또한 color, wheel, start()를 모두 사용해줬다고해도 선언한 타입에 맞게 인자를 주지 않으면 에러를 띄운다.

interface Car{
  color: string;
  wheels: number;
  start():void;
}

class BMW implements Car{
  color;
  wheels=4;
  start(){
    console.log('start 함수 실행!')
  }
}

 

상속 (implements)

정의한 BMW클래스를 가져와 새 인스턴스를 생성하고 사용하는 코드다.

interface Car{...}
class BMW implements Car{...}

// BMW 타입들을 가져와서 새로운 인스턴스를 만들어 변수 b에 넣음
const b = new BMW('green');
console.log(b);
b.start();

---------------------------------------------------
> BMW { wheels: 4, color: 'green' }
> start 함수 실행!

 

확장 (extends)

따라서 extends한 클래스를 사용할때 기존에 만들어놓은 타입들도 선언해줘야 에러가 나지 않는다.

interface Car { 
  color: string;
  wheels: number;
  start(): void;
}
class Bmw implements Car {...}

interface Benz extends Car{
  door:number;
  stop():void;
}

이전에 상속한 타입을 선언하지 않아서 생긴 에러

에러가 나오지 않게 정상적으로 작성한 코드

const benz : Benz = {
  door:5,
  stop(){console.log('stop 출력')},
  color:'yellow',
  wheels:5,
  start(){console.log('새로 선언한 start함수')}
}

benz.start();
benz.stop();
console.log(benz);
----------------------------------------------
> 새로 선언한 start함수
> stop 출력
> {
>   door: 5,
>   stop: [Function: stop],
>   color: 'yellow',
>   wheels: 5,
>   start: [Function: start]
> }

 

 

 

 

옵션 속성

인터페이스에 정의되어있는 속성을 모두 사용하지 않아도 되는데, 이를 옵션 속성이라고 한다.

속성 끝에 ?를 사용하며, 속성을 선택적으로 적용할 수 있다는 장점이 있다.

또한 인터페이스에 정의되어 있지 않은 속성에 대해 오류를 표시해준다.

interface 인터페이스_이름 {
  속성?: 타입;
}

 

 

 

 

읽기 전용 속성

인터페이스로 객체를 처음 생성할 때만 값을 할당하고 그 이후에는 변경할 수 없는 속성을 의미하며, 앞에 readonly 속성을 붙인다.

실수로 값을 지정하는 경우를 예방할 수 있다.

interface CraftBeer {
  readonly brand: string;
}

let myBeer: CraftBeer = {
  brand: 'Belgian Monk'
};

// 수정하려고하면 오류가 난다.
myBeer.brand = 'Korean Carpenter';

 

 

 

 

읽기 전용 배열

배열을 선언할때 ReadonlyArray<T> 타입을 사용하면 읽기 전용 배열을 생성할 수 있다.

역시 읽기 전용이므로 선언하는 시점에만 값을 정의할 수 있고, 내용을 변경할 수 없다.

let arr: ReadonlyArray<number> = [1,2,3];
arr.splice(0,1); // error
arr.push(4); // error
arr[0] = 100; // error

 

 

 

 

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

인터페이스를 이용하여 객체를 선언할때 좀더 엄밀히 속성검사를 한다.

interface CraftBeer {
  readonly brand: string;
}

function brewBeer(beer: CraftBeer) {...}
brewBeer({ brandon: 'what' }); // 이 안에 brand가 있어야한다.
// 에러메세지
// error: Object literal may only specify known properties, but 'brandon' does not exist in type 'CraftBeer'. Did you mean to write 'brand'?

타입추론을 무시하고 싶을때

interface CraftBeer { brand?: string; }
let myBeer = { brandon: 'what' }';

function brewBeer(beer: CraftBeer) {...;}
brewBeer(myBeer as CraftBeer);

인터페이스에 정의하지 않은 속성들을 추가로 사용하고 싶을때

interface CraftBeer {
  brand?: string;
  [propName: string]: any;
}

 

 

 

 

인터페이스 총정리

type Score = 'A' | 'B' | 'C' | 'F'

interface User {
  name : string;
  age : number;
  birth? : number;
  // [grade:number] : string; -> grade 배열 인덱스를 사용하는 인터페이스
  [grade:number] : Score; // 배열 안에 입력하는 값을 Score로 한정시킬때.
}

let user : User = {
  name = 'zz',
  age = 13,
  // birth는 ?을 통해 옵션속성으로 만들어놨기때문에 사용하지 않아도 에러가 뜨지않는다.
  1 : 'A',
  2 : 'C' 
}

user.age = 10;
user.gender = 'male'; // user에서 gender 속성을 선언한 적이 없기때문에 에러가 뜬다.

 

 

댓글