TypeScript | 인터페이스

Kate Jung·2022년 1월 10일
0

TypeScript

목록 보기
4/10
post-thumbnail

📌 개요

  • 인터페이스란?

    상호 간에 정의한 약속 혹은 규칙

  • 사용하는 경우

    프로퍼티를 정의해서 객체를 표현하고자 할 때

  • 타입스크립트에서의 인터페이스는 보통 다음과 같은 범주에 대해 약속을 정의 가능

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

📌 속성 관련 특징(vs object)

🔹 예시 1

◾ object

특정 속성 값에 대한 정보가 없음

// 객체: object 타입으로 정의 가능.
let user: object; // user라는 객체를 만듦.

user = {
  name: 'xx',
  age: 30
}

console.log(user.name) 
/* 
에러 발생 
- 메세지: Property 'name' does not exist on type 'object'.(2339)
- 이유: 👉 object에는 특정 속성 값에 대한 정보가 없음.*/

◾ interface

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

// 👉 속성을 적지 않은 경우 -> 에러 발생
let user: User = { // user는 User 타입으로 명시함.

} 
// 메세지: Type '{}' is missing the following properties from type 'User': name, age(2739)

// 👉 속성을 다 적은 경우 -> 에러 발생 x
let user: User = {
  name: 'xx',
  age: 30
}

console.log(user.age) 
/* 
- 결과: 30
👉 property 파악 가능: user. 작성한 상태에서 어떤 property들이 있는지 나온다. */

user.age = 10 // 👉 프로퍼티 값 바꾸기 가능
user.gender = 'male' // 👉 없는 프로퍼티 입력 시 -> 에러 발생

🔹 예시 2

◾ object

let person = { name: 'Capt', age: 28 };

function logAge(obj: { age: number }) { // 👉 인자의 형태: age를 속성으로 갖는 객체
  console.log(obj.age); // 28
}
logAge(person); // 28

◾ interface

인자를 받을 때 객체의 속성 타입까지 정의 가능

interface personAge {
  age: number;
}

function logAge(obj: personAge) { // 👉 인자의 형태: 명시적 ('personAge' 타입 필요)
  console.log(obj.age);
}
let person = { name: 'Capt', age: 28 };
logAge(person); // 28
  • 인터페이스에 정의된 속성, 타입의 조건만 만족하면 됨

    인터페이스를 인자로 받아 사용 시

    • 객체의 속성 갯수가 더 많아도 상관 x
    • 인터페이스에 선언된 속성 순서 상관 x

📌 옵셔널

  • 사용하는 경우

    있어도 되고 없어도 되는 경우

  • 형식

    • 속성의 끝에 ?를 붙임

      interface 인터페이스_이름 {
        속성?: 타입;
      }
  • 예시 코드

    interface User {
      name: string;
      age: number;
      gender?: string;
    }
    
    let user: User = {
      name: 'xx',
      age: 30
    }
    
    user.gender = 'male' // 나중에 추가

📌 읽기 전용

🔹 속성

인터페이스로 객체를 첫 생성할 때만 값 할당.
이후 변경 불가.

  • 문법

    앞에 readonly 붙임.

  • 예시 코드

    // ✨ 비교: 일반적인 경우
    interface User {
      birthYear: number
    }
    
    let user: User = {
      birthYear: 2000 // 작성 시, suggest 생김
    }
    
    user.birthYear = 1990; // 바꿔도 문제 없음.
    
    // ✨ 비교: readonly 프로퍼티 사용한 경우
    interface User {
      readonly birthYear: number
    }
    
    let user: User = {
      birthYear: 2000 // 👉 최초에 생성할 때만 할당 가능. 이후 수정 불가
    }
    
    user.birthYear = 1990; // [에러 발생] 이유: 읽기 전용 속성이라서 수정 불가함.

🔹 배열

배열을 ReadonlyArray로 선언 시, 배열 내용 변경 불가

  • 생성 방법

    • 배열 선언 시, ReadonlyArray<T> 타입 사용
    • 주의 : 선언 시점에만 값 정의 가능
  • 예시 코드

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

📌 선언 방식

🔹 타입 추론 무시

interface CraftBeer {
  brand?: string;
}

function brewBeer(beer: CraftBeer) {
  // ..
}

let myBeer = { brandon: 'what' }; // brand가 아닌 brandon 선언

// 선언 방식 (기존)
brewBeer(myBeer) // 오류 발생 (오탈자 점검 요구)

// 선언 방식 (타입 추론을 무시하고 싶을 경우)
brewBeer(myBeer as CraftBeer); // 오류 無

🔹 여러 속성 추가하기

  1. 에러 발생

    // ex. 학년 별 점수 기입
    
    interface User {
      // 1~4학년 까지 interface를 정의함.
      1: string;
      2: string;
      3: string;
      4: string;
    }
    
    // user가 2학년이라면 1학년 성적 밖에 없음.
    let user: User = {
      1: 'A'
    }
    /* 
    에러 발생.
    이유: 2~4가 입력이 안되어 있어서.*/
  2. 해결 | 옵셔널

    // [1. 해결] 1~4를 모두 옵셔널하게 만들기. 
    interface User {
      1?: string;
      2?: string;
      3?: string;
      4?: string;
    }
  3. 리팩토링 1 | 문자열 인덱스

    // [2. 리팩토링 1] 문자열 인덱스 서명?을 추가
    interface User {
      [grade:number]:string;
      // grade가 number인 이유: 학년은 숫자로 받을 것
      // 학점은 string
    }
    // 다른 단어 가능: grade 단어에 의미 없음.
    // 의미: number를 key로 하고 string을 value로 받는 property를 여러 개 받을 수 있다.
    
    let user: User = {
      1: 'A',
      2: 'B' // 에러 없이 정보 추가.
    }
  4. 리팩토링 2 | 문자열 리터럴 타입

    // [3. 리팩토링 2] '문자열 리터럴 타입' 활용
    type Score = 'A' | 'B' | 'C' | 'F';
    
    interface User {
      [grade:number]: Score;
    }
    /* 
    문자열 리터럴 타입: string으로 받기엔 범위가 넓을 때 사용
    - string: 어떤 문자열도 입력 가능
    - 문자열 리터럴 타입(Score): 해당 값 외에 입력 불가 */
    
    //  다른 값을 넣으면 에러 발생
    let user: User = {
      1: 's',
      2: 'a'
    }

📌 함수 정의 가능

  • 함수 타입 정의 시, 사용 가능
  • 함수의 인자 타입 & 반환 값 타입 정함.
  • ex 1. add 함수
    interface Add { // Add라는 인터페이스를 만듦.
      (num1:number, num2:number): number; // 내부: 괄호(인자 값), 리턴 값 적음.
    }
    
    const add:Add = function(x, y) { // 👉 interface의 인자명(ex. num1)을 다른 문자(ex. x)로 사용 가능
      return x + y
    }
    
    add(10, 20) // 30
    add(10, 20, 30) // [에러 발생] 인자 1개 더 적음
    add(10, '20') // [에러 발생] 다른 타입(string)으로 적음
  • ex 2. 성인 여부 판단 함수
    // 나이를 받아서 성인인지 아닌지 boolean값을 리턴해주는 함수 정의
    interface IsAdult {
      (age:number):boolean
    }
    
    // 19보다 크면 true, 아니면 false를 반환하도록 만듦.
    const a:IsAdult = (age) => {
      return age > 19
    }
    
    a<(33) // true

📌 클래스 정의 가능

클래스가 (일정 조건을 만족하도록) 타입 규칙 정하기 가능

  • implements 사용
    // Car라는 interface 정의
    interface Car {
      color: string;
      wheels: number;
      start(): void;
    }
    
    // Car를 이용해서 Bmw라는 클래스 만듦.
    class Bmw implements Car { // Car에 있는 속성 값을 모두 입력해야 함.
      color = 'red'
      wheels = 4
      start(){
        console.log('go')
      }
    }
    // [constructor 활용]
    class Bmw implements Car {
      color;
      constructor(c: string){ // 생성될 때 color를 입력 받음. c는 string이 되야 함.
        this.color = c;
      }
      wheels = 4;
      start(){
        console.log('go');
      }
    }
    
    const b = new Bmw('green')
    console.log(b)
    /*
    Bmw: {
      "wheels": 4,
      "color": "green"
    } 
     */
    b.start() 
    // "go"

📌 확장 (extends)

  • 사용하는 경우

    인터페이스는 확장 (여러 개) 가능. 이때 사용

1. 기본 (인터페이스 간 확장)

인터페이스 간 확장 가능

// Car라는 interface 정의
interface Car {
  color: string;
  wheels: number;
  start(): void;
}

interface Benz extends Car { // Car가 가지고 있던 속성들을 그대로 받게 됨.
  door: number // 추가로 정의 가능.
  stop(): void
}

const benz: Benz = { // Car & Benz의 속성 모두 입력해야 함. 아니면 에러 발생
  door: 5,
  stop(){
    console.log('stop')
  },
  color: "red",
  wheels: 4,
  start(){
    console.log('start')
  }
}
interface Person {
  name: string;
}
interface Developer extends Person {
  skill: string;
}
let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'TypeScript';

2. 동시 확장 (여러 인터페이스들 상속)

여러 인터페이스를 상속받아 사용 가능

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

interface Toy {
  name: string;
}

interface ToyCar extends Car, Toy { // 동시 확장
  price: number
}
interface Person {
  name: string;
}
interface Drinker {
  drink: string;
}
interface Developer extends Person,Drinker {
  skill: string;
}
let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'TypeScript';
fe.drink = 'Beer';

📌 하이브리드 타입

인터페이스도 여러 타입 조합하여 제작 가능
(자바스크립트의 (유연하고 동적인) 타입 특성에 따라)

  • ex. 함수 타입이면서 객체 타입을 정의 가능한 인터페이스
    interface CraftBeer {
      (beer: string): string;
      brand: string;
      brew(): void;
    }
    
    function myBeer(): CraftBeer {
      let my = (function(beer: string) {}) as CraftBeer;
      my.brand = 'Beer Kitchen';
      my.brew = function() {};
      return my;
    }
    
    let brewedBeer = myBeer();
    brewedBeer('My First Beer');
    brewedBeer.brand = 'Pangyo Craft';
    brewedBeer.brew();

참고

  • 캡틴판교_타입스크립트 핸드북
  • 코딩앙마_타입스크립트
profile
복습 목적 블로그 입니다.

0개의 댓글