ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Typescript] 인터페이스(Interface)
    Front-End(Web)/Typescript 2021. 2. 22. 19:12
    반응형

    이전 글에서부터, 타입의 커스터마이징에 대해 얘기하면서, 그리고 Type Aliase 를 공부하면서 Interface 가 종종 거론되었다.

    타입의 유형화에 유리한 이 인터페이스에 대해 한번 알아보도록 하자.


    💙 Interface 란?

    인터페이스는 특정 자료형의 구조를 나타낸 일종의 커스텀 타입이다.

    인터페이스는 Javascript같은 동적타입 언어환경에선 다뤄지지 않으나, 정적타입 언어인 Typescript는 타입검사가 요구되므로 이를 지원한다.

     

    - Interface 문법 및 적용

    'interface' 키워드를 통해 제작할 수 있다. 나머지는, 변수선언 문법과 유사하다.

    * Interface 이름 첫 글자는 대문자로 시작하도록 한다. 다른 정적언어는 I+인터페이스명을 권장하나, 타입스크립트는 해당되지 않음

    interface [인터페이스명] {
      ...
    }

    인터페이스는 컴파일된 JS파일에선 확인할 수 없다. 즉, Typescript가 컴파일될 때, 인터페이스를 제외시키는 것이다.

     

    interface StudentInfo {
      studentId: number,
      studentName: string,
      age: number,
      subject: string,
      graduated: boolean,
    }
    
    function getStudentMoreInfo(studentId: number): StudentInfo {
      return {
        studentId: 10,
        studentName: 'Taeng',
        age: 29,
        subject: 'Typescript',
        graduated: false,
      };
    }

    인터페이스 적용방법이다. StudentInfo 인터페이스를 제작한 뒤, 이를 getStudentMoreInfo() 함수 반환값의 타입으로 적용한다.

    인터페이스는 해당 형태의 타입으로 반환되는 것을 강제한다. (타입 불일치, 필드 누락 등등 시 에러 발생)

     

    - Interface vs Type Aliase

    두 문법 모두, 기존의 타입들을 활용해 커스텀 타입을 만든다는 공통점이 있다.

    앞의 타입에서 언급했듯, 둘의 차이는 Type Aliase는 필드추가 및 타입상속(&로 가능) 이 제한되나, Interface는 가능하다는 것이다.

    // Interface
    interface ButtonInterface {
      onInit():void;
      onClick():void;
    }
    
    interface ButtonInterface {
      onToggle():void;	// ButtonInterface 병함됨
    }
    
    
    // Type Aliase
    type ButtonType = {
      onInit():void;
      onClick():void;
    }
    
    type ButtonType = {
      onToggle():void;	// 오류: 'ButtonType' 식별자가 중복되었습니다.
    }

    💙 Interface 추가 문법

    1. 인터페이스의 메서드(method) 정의

    메서드는 객체 내에서 생성된 함수이다. 역시 마찬가지로, 함수 타입선언 방식을 통해 인터페이스에 포함될 수 있다.

    interface CommentBox {
      addComment (comment: string): string;
      addComment: (comment: string) => string;
    }

     

    2. 선택적 프로퍼티(Optional Properties)

    인터페이스에서 반드시 포함되지 않아도 되는 프로퍼티가 있을 것이다. 이를 설정하는 문법으로, '?' 기호를 키 뒤에 붙여주기만 하면 된다.

    선택적 프로퍼티의 이점은, 인터페이스에서 반드시 필요하지 않은 프로퍼티를 구분하는 한편, 이에 대한 오류를 확인 가능하단 것이다.

    interface StudentInfo {
      studentId: number,
      studentName: string,
      age: number,
      subject?: string,
      graduated: boolean,
    }
    
    function studyName(student: StudentInfo) :string {
      return hisSubject: string = student.subjet;		// error! subjet 프로퍼티명 존재하지 않음
    }

     

    3. 읽기 전용 프로퍼티(Read-only Properties)

    말 그대로 수정은 불가하고 읽기만 가능한 필드이다. 생성시 할당된 프로퍼티 값을 수정할 수 없다. 필드명 앞에 readonly 를 붙여준다.

    interface StudentInfo {
      readonly studentId: number,
      studentName: string,
      age: number,
      subject?: string,
      graduated: boolean,
    }
    
    function modifyStudentInfo (student: StudentInfo): void {
      student.studentId = 40,		// error! (읽기전용)
      student.graduated = true,
    }

     

    4. 함수 타입 인터페이스

    인터페이스는 함수 타입도 정의할 수 있다. 문법은, 중괄호 안에 (매개변수 타입): 반환값 타입; 형태로 만든다.

    interface FunctionalInterface {
      (n: number): number;
    }

     

    함수 타입 인터페이스를 미리 설정하면, 함수 선언시 별도의 타입설정이 불필요해진다.

    // Before: 함수타입 설정
    const factorial = (n:number): number => {
      if (n === 0) { return 0; }
      if (n === 1) { return 1; }
      return n * factorial(n - 1);
    }
    
    
    // After: 함수타입 인터페이스
    interface FactorialInterface {
      (n: number): number;  
    }
    
    const facto: FactorialInterface = (n) => {
      if (n === 0) { return 0; }
      if (n === 1) { return 1; }
      return n * facto(n - 1);
    };

     

    5. 인덱서블 타입 인터페이스

    인덱싱이 가능한 자료형을 의미한다. 문법은 대괄호 안에 [인덱스 타입]: 반환값 타입; 형태로 만든다.

    이러한 기술법을, 인덱스 시그니쳐(Index Signature) 라고 한다. 인덱스 시그니쳐의 인덱스 타입은 number 혹은 string 이어야만 한다.

    interface StringArray {
      [index: number]: string;
    }
    
    let myArray: StringArray;
    myArray = ["Bob", "Fred"];
    
    let myStr: string = myArray[0];

     

    * 이전의 객체 인터페이스에서, 프로퍼티 추가를 위해 작성했던 아래 예제도 인덱스 시그니처에 해당된다.

    interface ButtonInterface {
      onInit?():void;
      onClick():void;
      // 인덱스 시그니처
      [prop:string]: any;
    }

     

    6. 클래스 타입 인터페이스, 인터페이스 확장

    인터페이스는 클래스와 비슷하나, 정의만 할 뿐 실제 구현되지는 않는다. (일종의 추상 클래스)

    클래스를 통해서 인스턴스를 생성했을 때, 이것이 가져야 할 속성 또는 메서드를 정의할 수 있는 것이다.

    또한, 클래스의 extends 키워드로 하나의 클래스에 상속, implements 키워드를 통해 해당 인터페이스들을 클래스와 연결할 수 있다.

    // 클래스 => 인터페이스 상속
    class Control { 
      private state: any; 
      protected someState: any = 100; 
    } 
    
    interface SelectableControl extends Control { 
      select(): void; 
    }
    
    
    // 인터페이스 => 클래스 참조
    interface ClockInterface { 
      currentTime: Date; 
      setTime(d: Date): void; 
    } 
    
    class Clock implements ClockInterface { 
      currentTime: Date = new Date(); 
      setTime(d: Date) { 
        this.currentTime = d; 
      } 
      constructor(h: number, m: number) { } 
    }

     

    인터페이스 간에도 마찬가지로, extends(상속)을 통해 다양한 조합을 만들 수 있다.

    // 인터페이스 확장
    interface Shape {
      color: string;
    }
    
    interface Square extends Shape {
      sideLength: number;
    }
    
    
    // 인터페이스 다중 확장(복수)
    interface Shape {
      color: string;
    }
    
    interface Size {
      width: number;
      height: number;
    }
    
    interface DrawSquare extends Shape, Size {}
    
    let square = {} as DrawSquare;
    square.color = 'red';
    square.width = 20;

     Typescript의 클래스 문법에 대해서는 별도로 포스팅하며, 이전에 Javascript 클래스 공부가 선행되야 할 것 같다.

    (Typescript 가이드북 링크 : yamoo9.gitbook.io/typescript/classes )

     

    7. 하이브리드 타입

    인터페이스는 Javascript의 다양한 타입들을 적용할 수 있으며, 이러한 프로퍼티들을 복합적으로 수행할 수 있다.

    interface Counter {
        (start: number): string;
        interval: number;
        reset(): void;
    }
    
    function getCounter(): Counter {
        let counter = (function (start: number) { }) as Counter;
        counter.interval = 123;
        counter.reset = function () { };
        return counter;
    }
    
    let c = getCounter();
    c(10);			// 함수역할 수행 (counter 함수)
    c.reset();		// 객체역할 수행: 메서드 프로퍼티
    c.interval = 5.0;	// 객체역할 수행: 숫자타입 프로퍼티

    사실 Type Aliase와 근본적으로 큰 차이점이 느껴지진 않았다. 좀 더 세부적인 문법들로 구현할 수 있다는 차이점을 제외한다면?

    인터페이스 공부도 의미있었지만, 클래스 관련 부분에서 많은 부분을 생략했다. (public, private 개념, 전역 프로퍼티, 추상 클래스 등)

    이 부분을 별도로 공부하여 포스팅할 것이며, 이를 위해 Javascript ES6 클래스 자료형에 대한 공부부터 선행되어야 할 것 같다.

     

    [출처]

    - Typescript 핸드북 : typescript-kr.github.io/pages/basic-types.html#%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8-type-assertions  

    - Typescript 가이드북 : yamoo9.gitbook.io/typescript/types/type-assertions  

    - kjwsx23 님의 블로그 : kjwsx23.tistory.com/450?category=746259

     

    반응형
Designed by Tistory.