개발 일기

Interface ? 본문

컴퓨터 언어/Typescript

Interface ?

이건욱

Interface을 사용하여 추상 메소드를 정의하여 코드의 약속을 지정할 수가 있습니다.

 

[간단한 예시]

interface LabeledValue {
    label: string;
}

function printLabel(labeledObj: LabeledValue) {
    console.log(labeledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

여기에서 출력 값은 다음과 같이 "Size 10 Object"입니다.

 

만약에 myObj에서 label값이 없으면 에러가 발생합니다.

 

개발을 하다보면 interface에서 모든 값들이 필수일 필요는 없습니다.

그럴 때 사용할 수 있는게 Optional property 입니다.

 

[예시]

interface SquareConfig {
    color?: string;
    width?: number;
}
function createSquare(config: SquareConfig): {color: string; area: number} {
    let newSquare = {color: "white", area: 100};
    if (config.color) {
        newSquare.color = config.color;
    }
    if (config.width) {
        newSquare.area = config.width * config.width;
    }
    return newSquare;
}
let mySquare = createSquare({color: "black"});

여기에서 interface에서 ?을 포함하지 않았으면 밑에 mySquare에서 color 와 width을 같이 넘겨줘야 에러가 발생하지 않습니다.

 

Readonly Properties 

처음에 객체를 생성 한 후에 수정이 불가능 해야 할때가 있습니다. 그럴 때 사용하는게 Readonly Properties 입니다.

 

[예시]

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

 

Typescript에서는 Array<T>와 동일하지만 처음에 생성을 한 후에 내용을 변경하지 않아야 할때 사용 하는 ReadonlyArray<T>가 있습니다.

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!

a = ro as number[]; // Type assertion으로 변경했을 때에는 Override가 가능합니다.

readonly 와 const 중 차이는 readonly일 때는 객체를 주로 사용을 하고 const일 때는 변수일때 사용을합니다.

 

프로퍼티 접근 체크

interface SquareConfig {
    color?: string;
    width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
    // ...
}
let mySquare = createSquare({ colour: "red", width: 100 });

다음과 같은 코드가 있을 때 colour라고 잘 못 입력을 하였습니다. Typescript는 대상 타입이 없으면 에러가 발생합니다.

만약에 다음과 같은 코드가 정상적으로 돌아가기 위해서는 Type assertion을 사용하는 방법이 있습니다.

let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);

 

Functin Types

인터페이스는 프로퍼티를 가진 객체 이외에, 함수 타입을 선언하는데 이용될 수도 있습니다.

interface SearchFunc {
    (source: string, subString: string): boolean;
}

매개 변수 목록과, 반환 타입만 주어진 함수 선언과 같은 형태로 작성합니다.

아래와 같은 형태로 함수 인터페이스를 사용합니다.

매개 변수의 이름이 일치할 필요는 없습니다.

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    let result = source.search(subString);
    return result > -1;
}
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
    let result = src.search(sub);
    return result > -1;
}

리턴 값이 숫자 혹은 문자열 등 다른 타입인 경우 경고가 출력됩니다.

Class Types

C# 및 Java와 같은 언어로 인터페이스를 사용하는 가장 일반적인 방법 중 하나는 클래스가 특정 계약을 준수하도록 명시적으로 적용하는 것입니다. TypeScript에서도 가능합니다.

 

interface ClockInterface {
    currentTime: Date;
}

class Clock implements ClockInterface {
    currentTime: Date = new Date();
    constructor(h: number, m: number) { }
}
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) { }
}

위의 예제에서 setTime과 마찬가지로 클래스에 구현된 인터페이스의 메소드를 설명할 수도 있습니다.

 

인터페이스는 클래스의 public private이 아니라 public만을 기술할 수 있습니다. 그렇기 때문에 클래스를 사용하여 클래스 인스턴스의 private에 특정 타입이 있는지 확인할 수 없습니다.

Extending Interfaces

클래스처럼 인터페이스도 확장이 가능합니다.

[예제]

interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

let square = {} as Square;
square.color = "blue";
square.sideLength = 10;
interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = {} as Square;
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

 

Hybrid Types

메서드와 프로퍼티 모두를 가진 하이브리드 타입의 인터페이스도 있습니다.

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}
function getCounter(): Counter {
    let counter = <Counter>function (start: number) { };
    counter.interval = 123;
    counter.reset = function () { };
    return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

Interfaces Extending Classes

인터페이스가 클래스를 확장하면 멤버를 상속하지만 Implements은 상속하지 않습니다.

이 패턴은 private 및 protected 멤버도 상속합니다.

private 또는 protected 멤버가 있는 클래스를 확장하는 인터페이스는 해당 클래스 또는 해당 클래스의 하위 클래스에서만 구현 가능합니다.

[예시]

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// Error: Property 'state' is missing in type 'Image'.
class Image implements SelectableControl {
    private state: any;
    select() { }
}

위의 예제에서, SelectableControl은 private state 속성을 포함하여 Control의 모든 멤버를 포함합니다.

state는 private 멤버이기 때문에 Control의 자손만 SelectableControl을 구현할 수 있습니다.

왜냐하면 Control의 자손들만이 같은 선언에서 유래된 private state 멤버를 가질 것이기 때문입니다.

이는 private 멤버들이 호환 가능해야 한다는 요구 사항입니다.

Control 클래스 안에서 SelectableControl 인스턴스를 통해 private state 멤버에 접근할 수 있습니다.

효율적으로 SelectableControl은 select 메소드를 가진 것으로 알려진 Control과 같은 역할을 합니다. 

Button과 TextBox 클래스는SelectableControl의 하위 타입입니다. (왜냐하면 둘 다 Control을 상속 받았고 select 메소드를 가졌기 때문입니다), 하지만 Image 클래스와 Location 클래스는 그렇지 않습니다.

'컴퓨터 언어 > Typescript' 카테고리의 다른 글

Literal Types ?  (0) 2020.05.03
Function ?  (0) 2020.05.03
Class ?  (0) 2020.05.01
변수 선언 ?  (0) 2020.04.26
기본 Type ?  (0) 2020.04.18
Comments