JavaScript/TypeScript

TypeScript | Interface 인터페이스

pathas 2020. 5. 7. 12:34

Interface 인터페이스

타입스크립트에서 인터페이스는 여러 가지 자료형의 타입을 정의하는 용도로 사용

인터페이스로 객체 타입 정의하기

interface 키워드를 사용해서 인터페이스로 타입 정의

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

const p1: Person = { name: 'mike', age: 23 };
const p2: Person = { name: 'mike', age: 'ten' }; // 타입 에러
  • Person 인터페이스로 객체 내부에 존재하는 각 속성의 타입 정의
  • 하나 이상의 속성 타입을 만족하지 못하면 타입 에러 발생

선택 속성

선택 속성은 객체에 없어도 되는 속성을 말함
인터페이스에서 선택 속성은 ?(물음표) 기호 사용

interface Person {
    name: string;
    age?: number;
}
const p1: Person = { name: "mike" };

// 선택 속성 대신 유니온 타입으로 undefined를 추가하는 경우
interface Person {
    name: string;
    age: number | undefined;
}
const p1: Person = { name: "mike" }; // 타입 에러
const p2: Person = { name: "mike", age: undefined };
  • 유니온 타입으로 undefined를 추가하는 경우 선택 속성과 달리 명시적으로 undefined를 입력해야 함

읽기 전용 속성

읽기 전용 속성은 값이 변하지 않는 속성을 말함
인터페이스에서 읽기 전용 속성은 readonly 키워드 사용

interface Person {
    readonly name: string;
    age?: number;
}
const p1: Person = {
    name: 'mike',
}
p1.name = 'jonh'; // 컴파일 에러
  • 변수를 정의하는 시점에는 값 할당 가능
  • 읽기 전용 속성의 값을 수정하려고 하면 컴파일 에러가 발생함

정의되지 않은 속성값을 할당하는 경우

보통은 객체가 인터페이스에 정의되지 않은 속성값을 갖고 있어도 할당이 가능하지만
리터럴로 초기화하는 경우에는 타입 에러 발생

interface Person {
    readonly name: string;
    age?: number;
}

// 리터럴로 초기화하는 경우
const p1: Person = {
    name: 'mike',
    birthday: '1997-01-01', // 타입 에러
}
const p2 = {
    name: 'mike',
    birthday: '1997-01-01',
}
const p3: Person = p2;
  • Person 인터페이스에 정의되지 않은 birthday 속성을 리터럴로 입력하므로 타입 에러 발생
    → 리터럴에서 에러가 발생하는 이유는 프로그래머의 실수일 확률이 높기 때문이며, 이는 타입스크립트의 편의 기능임
  • p2는 Person에 정의되지 않은 속성을 포함하지만 p3에 할당될 때 타입 에러가 발생하지 않음
    → p3 타입이 p2의 타입을 포함하는 더 큰 타입이기 때문('타입 호환성'에서 다룸)

인터페이스로 인덱스 타입 정의하기

인터페이스에서 속성 이름을 구체적으로 정의하지 않고 값의 타입만 정의하는 것을 인덱스(index) 타입이라고 함

interface Person {
    readonly name: string;
    age: number;
    // 인덱스 타입
    [key:string]: string | number;
}
const p1: Person = {
    name: 'mike',
    birthday: '1997-01-01',
    age: '25', // 타입 에러
}
  • 문자열로 된 속성 이름에 대해 값이 문자열 또는 숫자라고 정의
  • birthday 속성을 리터럴로 정의해도 컴파일 에러가 발생하지 않음
  • age는 명시적으로 숫자로 정의했기 때문에 문자열 입력 시 타입 에러 발생

여러 개의 인덱스를 정의하는 경우

자바스크립트에서는 속성 이름에 숫자와 문자열을 사용할 수 있는데,
속성 이름이 숫자인 경우 문자열로 변환된 후 사용됨
따라서 타입스크립트에서는 숫자인 속성 이름의 값이 문자열인 속성 이름의 값으로 할당 가능한지 검사

interface YearPriceMap {
    [year: number]: number;
    [year: string]: string | number;
}
const yearMap: YearPriceMap = {};
yearMap[1998] = 1000;
yearMap[1998] = 'abc'; // 타입 에러
yearMap['2000'] = 1234;
yearMap['2000'] = 'million';
  • YearPriceMap 에서 number 는 string | number에 할당 가능하기 때문에 타입 에러가 발생하지 않음
  • 숫자인 속성 이름이 문자열로 변환되기 때문에 숫자 속성의 값이 문자 속성의 값에 포함될 수 있어야 하는 것
  • 속성 이름이 숫자인데 문자열을 값으로 입력하면 타입 에러 발생

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

인터페이스로 함수를 정의할 때는 속성 이름 없이 정의

interface GetInfoText {
    (name: string, age: number): string;
}
const getInfoText: GetInfoText = function(name, age) {
    const nameText = name.substr(0, 10);
    const ageText = age >= 35 ? 'senior' : 'junior';
    return `name: ${nameText}, age: ${ageText}`;
}

인터페이스로 클래스 구현하기

implements 키워드를 사용해서 인터페이스를 클래스로 구현

interface Person {
    name: string;
    age: number;
    isYoungerThan(age: number): boolean;
}

class SomePerson implements Person {
    name: string;
    age: number;
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
    isYoungerThan(age: number) {
        return this.age < age;
    }
}
  • 세 개의 속성을 가진 인터페이스 정의
  • implements 키워드를 이용해서 인터페이스를 클래스로 구현
  • 하나의 속성이라도 구현하지 않으면 컴파일 에러 발생
  • name, age 속성은 필수 속성이기 때문에 생성자에서 값을 할당하지 않으면 컴파일 에러 발생

※ 타입스크립트에서 클래스의 타입을 정의하기 위해서는 알아야할 내용이 많지만

리액트에서는 객체지향 프로그래밍을 할 일이 많지 않고

리액트 훅의 등장으로 클래스형 컴포넌트의 중요성이 낮아짐


인터페이스 확장

extends 키워드를 사용해서 인터페이스 확장 가능
여러 개의 인터페이스를 확장할 수도 있음

interface Name {
    name: string;
}
interface Age {
    age: number;
}
interface Korean extends Name, Age {
    isLiveInSeoul: boolean;
}

/*
interface Korean {
    name: string;
    age: number;
    isLiveInSeoul: boolean;
}
*/
  • Name, Age 인터페이스를 확장해서 Korean 인터페이스 생성
  • 확장해서 만들어진 Korean 인터페이스는 주석 처리한 것과 같음

인터페이스 합치기

& 기호(교차 타입)를 사용해서 여러 인터페이스를 하나로 합칠 수 있음

interface Person {
    name: string;
    age: number;
}
interface Product {
    name: string;
    price: number;
}
type PP = Person & Product;
const pp: PP = {
    name: 'a',
    age: 23,
    price: 1000,
}
  • type PP는 합쳐진 두 인터페이스 Person과 Product의 모든 속성값을 포함
  • PP의 타입이 name 속성만 포함하지 않는 것은 PP 타입이 Person과 Product 속성의 교집합이 아니라
    각 인터페이스의 타입이 가질 수 있는 값의 집합에 대한 교집합이기 때문

참고도서: [실전 리액트 프로그래밍 / 저자_ 이재승 / 출판사_ 프로그래밍 인사이트]