티스토리 뷰

개발공부/🟦 TypeScript

[TS] Interface

2022. 11. 7. 15:37

1️⃣  Interface란?

 

일반적으로 변수, 함수, 클래스에 타입 체크를 위해 사용된다.

 

 

-  직접 인스턴스를 생성할 수 없고 모든 메소드가 추상 메소드이다.

-  추상 클래스의 추상 메소드와 달리 abstract 키워드는 사용할 수 없다.

-  ES6는 interface를 지원하지 않지만 TypeScript는 interface를 지원한다.

 

function sayName(obj: { name: string }) {
    console.log(obj.name);
}

let person = { name: "june" };
sayName(person);

위 코드를 interface로 표현하면 아래와 같다.

interface Person {
    name: string
}

function sayName(obj: Person) {
    console.log(obj.name);
}

let person = { name: "june" };
sayName(person);

-  Interface를 추가하여 함수 매개변수 프로퍼티를 정의할 수 있다.

-  정의한 프로퍼티 값을 누락하거나 정의하지 않는 값을 인수로 전달 시 컴파일 에러가 발생한다.

 

 

 

 

 

Interface를 사용하는 이유

 

-  타입의 이름을 짓고 코드 안의 계약을 정의한다.

-  프로젝트 외부에서 사용하는 코드의 계약을 정의하는 강력한 방법이다.

-  다음과 같은 범주에 대해 계약을 정의할 수 있다.

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

 

 

 

 

 

 

2️⃣  Properties

 

-  컴파일러는 프로퍼티의 두 가지 요소를 검사한다.

  • 필수요소 프로퍼티의 유무
  • 프로퍼티 타입

 

-  아래 예약어로 프로퍼티를 세밀하게 컨트롤 할 수 있다.

  • ? (Optional Properties)
  • readonly (Readonly properties)

 

 

 

▶  ? (Optional Properties)

-  프로퍼티 선언 시 이름 끝에 ?를 붙여서 표시

-  인터페이스에 속하지 않는 프로퍼티의 사용을 방지하면서 사용 가능한 프로퍼티를 기술할 때 사용

-  객체 안의 몇 개의 프로퍼티만 채워 함수에 전달하는 "option bags" 같은 패턴에 유용

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" });    // width 프로퍼티를 누락해도 에러가 나지 않음

 

 

 

 

▶  Readonly properties

-  객체가 처음 생성될 때만 값 설정이 가능하고, 이후 수정이 불가능

-  프로퍼티 이름 앞에 readonly를 붙여 사용

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

let point: Point = { x: 10, y: 20 };
point.x = 5;    // Cannot assign to 'x' because it is a read-only property.

 

 

💡  readonly  vs  const

readonly와 const 모두 생성 후에 배열을 변경하지 않음을 보장한다.

→  변수에는 const를, 프로퍼티에는 readonly를 사용

let arr: number[] = [1, 2, 3, 4];
let readonly_arr: ReadonlyArray<number> = arr;

readonly_arr[0] = 12;        // Error
readonly_arr.push(5);        // Error
readonly_arr.length = 100;   // Error

 

 

 

 

 

 

 

3️⃣  interface types

 

TypeScript에서 인터페이스는 함수, 클래스에서 사용할 수 있다.

 

 

▶  함수 (function type)

-  JavaScript 객체가 가질 수 있는 넓은 범위의 형태를 기술한다.

-  프로퍼티로 객체를 기술하는 것 외에도, 인터페이스는 함수 타입을 설명한다.

  • 함수의 인자의 타입과 반환 값의 타입 정의
  • 함수의 타입을 정의할 때에도 사용
interface SearchFunc {
    (source: string, subString: string): boolean
}

// 변수로 직접 함수 값이 할당되었기 때문에 인수 타입 생략 가능
// TypeScript의 문맥상 타이핑 (contextual typing)이 인수 타입 추론
let mySearch: SearchFunc;
mySearch = function (src, sub) {
    let result = src.search(sub);
    return result > -1;
}

// error: Type '(src: string, sub: string) => string' is not assignable to type 'SearchFunc'.
// Type 'string' is not assignable to type 'boolean'.
mySearch = function (src, sub) {
    let result = src.search(sub);
    return "string";
}

 

💡  문맥상 타이핑 (contextual typing)

 

타입 추론 | 타입스크립트 핸드북

타입 추론(Type Inference) 타입 추론이란 타입스크립트가 코드를 해석해 나가는 동작을 의미합니다. 타입 추론의 기본 타입스크립트가 타입 추론을 해나가는 과정은 다음과 같습니다. 위와 같이 x

joshua1988.github.io

 

 

 

 

 

▶  클래스 (class type)

-  클래스가 특정 통신 프로토콜을 충족하도록 명시적으로 강제한다. (클래스가 특정 계약(contract)을 충족하도록 명시적으로 강제)

-  C#과 Java와 같은 언어에서 일반적으로 인터페이스를 사용하는 방법과 동일하다.

interface Animal {
    makeSound(): void
}

class Dog implements Animal {
    makeSound(): void {
        console.log("멍멍");
    }
}

 

 

 

 

▶  interface 확장(상속)

-  클래스와 마찬가지로 인터페이스도 인터페이스 간의 확장(상속)이 가능하다.

interface Animal {
    makeSound(): void
}

interface Dog extends Animal {
    speed: number
}

class Bulldog implements Dog {
    speed: number
    makeSound(): void {
        console.log("멍멍");
    }
}

 

 

 

 

▶  hybrid type

-  자바스크립트의 유연하고 동적인 타입 특성에 따라 인터페이스도 여러 가지 타입을 조합할 수 있다.

-  아래 코드와 같이, 함수 타입이면서 객체 타입을 정의할 수 있는 인터페이스도 구현 가능하다.

interface Counter {
    (start: number): string
    interval: number
    reset(): void
}

function getCounter(): Counter {
    let counter = function (start: number) {} as Counter
    counter.interval = 123;
    counter.reset = function () {}
    return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

 

 

 

 

 

 

4️⃣  디자인 패턴 (Strategy pattern)

 

 

interface를 활용한 디자인 패턴 (Strategy pattern)

 

객체가 할 수 있는 행위들을 전략(strategy)으로 만들어 두고,

동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 수정이 가능하도록 만든 패턴이다.

 

이미지출처 : 엘리스 타입스크립트 I 강의자료

 

 

 

 

디자인 패턴 활용 사례

 

자판기 결제 방법을 현금 결제에서 카드 결제로 변경할 때, Pay 메소드 구현 변경이 필요하다.

class VendingMachine {
    pay() {
        console.log("cash pay!");
    }
}

 

메소드 수정 방식의 문제점

1)  OCP를 위배한다. (OOP 설계 원칙)

2)  시스템이 커져서 확장 될 경우 연동되는 시스템에도 영향을 줄 수 있다.

 

 

→  디자인 패턴으로 문제를 해결할 수 있다.

interface PaymentStrategy {
    pay(): void
}

class CardPaymentStrategy implements PaymentStrategy {
    pay(): void {
        console.log("card pay!");
    }
}

class CashPaymentStrategy implements PaymentStrategy {
    pay(): void {
        console.log("cash pay!");
    }
}

class VendingMachine {
    private paymentStrategy: PaymentStrategy
    setPaymentStrategy(paymentStrategy: PaymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    pay() {
        this.paymentStrategy.pay();
    }
}

const vendingMachine = new VendingMachine();

vendingMachine.setPaymentStrategy(new CashPaymentStrategy());
vendingMachine.pay();    // cash pay!

vendingMachine.setPaymentStrategy(new CardPaymentStrategy());
vendingMachine.pay();    // card pay!

1)  PaymentStrategy 인터페이스 구현

 

2)  각 결제수단(card, cash)에 따른 클래스가 PaymentStrategy를 implements 한다.

→  추상메서드 pay()를 각 클래스에서 구체화해야함

 

3)  VendingMachine 클래스에서 paymentStrategy 변수를 선언, 외부에서 클래스 주입이 가능하도록 setPaymentStrategy() 함수 구현

 

4)  VendingMachine 클래스의 pay() 메서드 구현 (paymentStrategy의 pay() 메서드가 호출되도록 구현)

→  pay() 메서드가 호출되면 주입된 paymentStrategy에 따라 다른 pay() 메서드가 호출된다.

 

다른 결제수단이 추가가 되어도 새로운 클래스만 구현해주면 되니 VendingMachine 내부의 pay 메서드를 수정할 필요가 없어졌다.

 

 

 

 

 

 


 이 글은 엘리스의 AI트랙 5기 강의를 들으며 정리한 내용입니다.

반응형
프로필사진
개발자 삐롱히

프론트엔드 개발자 삐롱히의 개발 & 공부 기록 블로그