TypeScript. 타입 종류(Interface)

cm·2024년 1월 24일

타입스크립트

목록 보기
8/9

타입종류 (인터페이스 타입)

  • 타입을 정의하는데 사용된다.
  • 타입형태를 확인하여 객체를 할당해야 한다. (중첩 인터페이스 포함)

인터페이스와 타입별칭의 차이점

  • 확장여부 : 인터페이스는 extends를 통해 상위인터페이스로부터 상속받아 확장가능하다. 그러나, 타입별칭은 &를 이용한다.
  • 병합여부 : 인터페이스는 여러군데에서 선언해서 병합가능하나 타입별칭은 한군데에서 선언한다.

1. 정의

1-1. 속성정의

  • 필수속성 : 반드시 기입해야 하는 속성
  • 선택속성 : 생략해도 되는 속성 (생략시 디폴트값: undefined)
  • 읽기전용속성(readonly) : 객체의 속성읽기 전용으로 변환(수정불가)
// 필수속성은 pages, 선택속성은 author
interface Book {
  pages: number;
  author?: string;  
}
const missing: Book = {
  pages: 80,
};
console.log(missing.author) // undefined

// 읽기전용 속성
interface Page {
  readonly text: string;
}
function read(page: Page) {
  console.log(page.text); // Ok
  page.text += '!'; // Error: Cannot assign to 'text' because it is a read-only property.
}

1-2. 함수정의

  • 동일한 이름으로 다른 시그니처를 가진 메서드 정의 가능하다. (함수 오버로드 발생)
  • 인터페이스 멤버를 함수로 선언하는 2가지 방법
    • 메서드 구문정의 : method() : string
    • 속성 구문정의:property: () => string;
    • 함수 타입 : (input : string) => number
    • 호출 시그니처 : (input : string) : number

1-3. 인덱스 시그니처 정의/사용방법

  • 인터페이스 객체가 임의의 키를 받고 해당 키 아래의 특정 타입을 반환한다.
  • 모든 객체의 프로퍼티키는 내부적으로 문자열로 변환되어 사용된다.

정의방법 : [key : type] : type

// 정의
interface WordCounts {
    [i: string]: number;
}
const counts: WordCounts = {};
counts.apple = 0;
counts.banana = 1;
counts.cherry = true; //Error

멤버접근

  • 멤버접근시 해당 속성이 없으면 새로 생성된다. 따라서, 인덱스 시그니처를 사용하면 프로퍼티의 존재유무를 알 수 없다.키-쌍을 저장하려는데 키를 미리 알 수 없다면 Map을 사용하는 것이 안전하다.

    // 값 할당
    interface DateByName {
        [i: string]: Date;
    }
    const a: DateByName = {
        frank: new Date("1 January 1818"),
    };
    a.frank; // 타입은 Date
    a.hello; // 타입은 Date, 런타임은 undefined
    console.log(a.frank, a.hello); // 1817-12-31T15:32:08.000Z undefined
    // 객체로 매핑하여 관리
    interface Counts {
      [key: string]: number
    }
    
    const counts: Counts = {};
    
    counts.apple = 3;
    counts.banana = 5;
    
    function setCount(counts: Counts, fruit: string) {
        counts[fruit] += 10;
    }
    setCount(counts, 'peach');
    console.log(counts);
    // {
    //   "apple": 3,
    //   "banana": 5,
    //   "peach": null
    // } 
    

예시 (속성과 인덱스 시그니처 혼합)

// 속성과 인덱스 시그니처 혼합
interface history {
   oro: number;
   [i: string]: number | string; // 명명된 속성의 타입 할당
}
const novels: history = { out: 1991, oro: 1688 };
const missing: history = { out: 1991 }; // Property 'oro' is missing in type '{ out: number; }' but required in type 'history'.
const konovels: history = { out: 1991, in: "in", oro: 1688 }; // 명명된 속성의 타입이 인덱스 시그니처에 할당될 수 있는 경우 더 구체적인 타입을 지정

숫자인덱스 시그니처

  • 타입스크립트에서는 모든 객체의 프로퍼티키는 내부적으로 문자열로 변환되어 사용된다. 따라서, 숫자인덱스 시그니처를 사용하기 위해서는 string 타입의 인덱스 시그니처를 만들어서 숫자 인덱스 시그니처가 반환하는 타입을 포함하도록 해야한다.
interface More {
    [i: number]: string;
    [i: string]: string | undefined;
}
// interface More {
//     [i: number]: string | undefined;
//     [i: string]: string  // Error : 최소한 string | undefined를 가져야함
// }
const mixed: More = {
    0: "",
    key1: "",
    key2: undefined,
};

2. 인터페이스 확장

  • extends 키워드를 이용하여 확장한다. (,를 이용해 다중확장 가능)
  • 상위 인터페이스를 확장한 경우 하위 인터페이스에 상위 인터페이스의 속성을 모두 포함시켜야한다.
  • 상위 인터페이스의 속성타입이 union이라면, 하위 인터페이스에는 이보다 더 구체적인 타입으로 속성타입을 정의한다.
  • 상위 인터페이스와 하위 인터페이스에 없는 속성을 추가하려면 타입단언을 하거나 변수선언을 하면 된다.
// 인터페이스 확장시 하위 인터페이스의 속성 : 상위를 모두 포함하여!
interface writing {
    title: string;
}
interface nov extends writing {
    pages: number;
}
let mynove: nov = {
    title: "hi",
    pages: 200,
};

// 인터페이스 확장시 하위 인터페이스의 속성타입 : 상위보다 더 좁게!
interface A {
    name: string | null | number;
}
// 상속받은 타입중에서만 타입 재정의가 가능하다. 
interface B extends A {
    name: string;
}

3. 인터페이스 병합

  • 같은 이름의 인터페이스가 동일한 스코프에 여러 곳에서 선언될 때, 컴파일러가 이를 자동으로 병합하여 하나의 인터페이스로 처리하는 기능이다. 병합된 인터페이스는 선언된 각 부분의 멤버를 합친다.
  • 여러 곳에서 선언하면 코드를 이해하기 어려우므로 비권장한다.
interface Merged {
    name: string;
}
interface Merged {
    age: number;
}
// 즉,
// interface Merged{
//     name : string
//     age :number
// }
let merged: Merged = { name: "1", age: 1 };
profile
나를 위한 기록

0개의 댓글