TypeScript Interface

Interfaces

TypeScript Interface 설명

목차

  1. Introduction

  2. Optional properties

  3. Readonly properties

  1. Excess preperty checks

  2. Function types

  3. Indexable types

  4. Class Types

  5. Extending interfaces

  6. Hybrid Types

  7. Interfaces extending Classes

Introduction

타입스크립트의 핵심 원칙은 타입체킹이 value가 가지는 모양에 초점을 맞추고 있다는 점이다. 이것은 duct typeing 혹은 structural subtyping 이라고도 불린다. 타입스크립트에서는 interface들은이런 타입들에 이름을 붙이는 역할을 하고, 당신의 혹은 다른사람과의 프로젝트에서 계약을 결정짓는 강력한 기능을 한다.

우리의 첫번째 interface

인터페이스가 어떻게 작동하는지 이해하는 가장 쉬운 방법은 아래의 예시를 시작하는 것이다.

    function printLabel(labelledObj: { label : string})
    {
        console.log(labelledObj.label);
    }

    let myObj = { size: 10, label: 'Size 10 Object' };

    printLabel(myObj);

타입체커는 printLabel 함수를 불러온다. printLabel 함수는 하나의 인자값을 가지고 있는데, 이 인자값은 label이라는 내부 prop을 가지고 string타입을 가지길 요구한다. 주목하라. MyObj가 label이외의 값을 가지고 있다는 점에. 컴파일러는 적어도 하나 이상의 요구된 값의 타입이 맞는지 확인한다. 몇가지 타입스크립트가 허용하지 않는 가지수가 있는데 그것들은 나중에 다룰 것이다.

주석 : 타입스크립트 컴파일러는 요구된 값의 이름과 타입이 일치하기만 한다면 다른 값이 추가되어도 상관하지 않는다고 본다. 단, 인자값으로 넘어가서 함수 내에서 사용하려한다면 error를 발생시킬 것이다.

우리는 같은 예시를 다시 쓸수 있는데, 이번에는 우리는 interface를사용하여 내부에 있는 label 이 string 타입을 요구한다는 것을 구현할 수 있다.

interface LabelledValue {
    label: string;
}

function printLabel(labelledObj: LabelledValue)
{
    console.log(labelledObj.label);
}

let myObj = { size: 10, label: 'Size 10 Object' };

printLabel(myObj);

구현의 방식은 위에서 설명했던 첫번째 예제와 비슷하다. 이번 예제도 역시 마찬가지로 printLabel의 인자값으로 동일하게 요소값이 lable인 string 타입의 값을 요구한다. 하지만 구현시에 처음 예제와 같이 직접 인자값에 요구값과 타입을 명시하지 않고, LabelledValue라는 interface 이름으로 넘겼다는 점이 차이다. 타입스크립트의 interface에서 중요한 점은 myObj가 interface를 implements 하지 않았음에도 printLabel에서 사용할 수 있다는 점이다. 다른 언어의 경우에는 myObj가 반드시 LabelledValue를 implements 해야만 printLabel에 인자값으로 넣을 수 있다는점과 차이가 있으며, 타입스크립트가 interface를 보는 것이 아니라 interface가 구현하는 값이 무엇인지 확인한다는 점을 알 수있다.

한가지 지적해야할 점은 interface가 요구하는 값의 순서가 상관이 없고, 오로지 객체가 그 값들의 타입을 만족 시키는지만 확인한다.

Optional Properties

모든 prop이 필수 요소가 아닐 수도 있다. 어떤 경우에는 있을수도, 그리고 없을 수도 있는 prop이 있을 수도 있다. 함수의 인자값으로 넘기는 object에서 오직 몇개의 요소만이 interface의 요소를 만족시킬 경우에 optional bags와 같은 패턴을 만들때 이런 optional prop는 유용하다.

여기 이 패턴의 예시를 적어놨다.

interface SqureConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SqureConfig) : {color: string; area:number} {
    let newSqure = {color: 'white', area: 100};
    if(config.color) {
        newSquare.color = config.color;
    }
    if(config.width) {
        newSquare.area = config.width ** 2;
    }

    return newSquare;
}

let mySquare = createSquare({color:'black'});

Interfaces 의 표기방식은 다른 interface들의 구현방식과 비슷하지만, : 왼쪽에 property 이름 바로 오른쪽 끝부분에 ?를 표기함으로써 구현할 수있다.

optional interface의 장점은 optional인 인자값이 있던 없던, interface이외의 값이 인자값으로 넘어 갔을 경우에 error를 발생시킬 수 있다는 점이다. 예를 들자면 아래와 같은 코드에서는 에러가 발생한다.

interface SqureConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SqureConfig) : {color: string; area:number} {
    let newSqure = {color: 'white', area: 100};
    if(config.colr) {
        // Error: Property 'clor' does not exist on type 'SquareConfig'
        newSquare.color = config.colr;
    }
    if(config.width) {
        newSquare.area = config.width ** 2;
    }

    return newSquare;
}

let mySquare = createSquare({color:'black'});

주석 : 만약 {color:'black', something:20} 으로 인자 obj를 직접 적어줬다면 something이 interface에 없기 때문에 에러가 터진다. 하지만 대신 let myObj = {color:'black', something:20}을 선언하고 인자값으로 createSquare(myObj)를 할경우 error가 나지 않는다. 동일한 obj이지만 literal일경우 함수가 호출될때 걸러내지만, 그렇지 않을 경우 걸러내지 않는다. 단, function내부에서는 오로지 interface의 prop만 사용 가능하기때문에 다른 값이 들어간다고 하더라도 문제가 없다.

Readonly properties

몇몇 prop들은 오직 처음에 만들어졌을 상황으로 유지되어야 할 때가 있다. 당신은 이런 환경을 readonly를 prop 이름 앞에 붙임으로서 만들어 낼 수 있다.

interface Point {
    readonly x: number;
    readonly y: number;
}

당신은 Point를 object literal에 할당할수가 있다. 할당 이후에는 x와 y값의 변경은 불가능해진다.

let p1: Point = { x: 10, y: 20};
p1.x = 5; //error

타입스크립트는 배열역시 readonly로 만들 수 있는 interface를 구비하고 있다. ReadyonlyArray를 선언함으로써 Array내부에 선언된 값들을 변경시킬 수 없게 만든다.

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // -> error
ro.push(5) // -> error
ro.length = 100 // -> error
a = ro; // error

마지막 라인에서 ReadonlyArray의 reference 할당조차 컴파일러가 error로 걸러낸다는 것을 볼 수 있다. 그렇지만 referece를 할당하는 방법이 있는데 타입 캐스팅을 통해 할당이 가능하다.

a = ro as number[];

readonly vs const

readonly또는 const를 써야할지 말지 기억하는 가장 쉬운 방법은 당신이 변수를 쓰고 있는건지 아니면 prop를 쓰고 있는지다. const는 변수고 readonly는 prop에 사용한다.