티스토리 뷰
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)
▶ 클래스 (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)으로 만들어 두고,
동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 수정이 가능하도록 만든 패턴이다.
디자인 패턴 활용 사례
자판기 결제 방법을 현금 결제에서 카드 결제로 변경할 때, 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기 강의를 들으며 정리한 내용입니다.
'개발공부 > 🟦 TypeScript' 카테고리의 다른 글
[TS] Generic (0) | 2022.11.08 |
---|---|
[TS] 객체 지향 프로그래밍 (OOP) (0) | 2022.10.26 |
[TS] TypeScript를 이용해 함수 사용하기 (0) | 2022.10.09 |
[TS] 타입스크립트의 기본 Type (0) | 2022.10.09 |
[TS] 타입스크립트를 사용하는 이유 (0) | 2022.10.09 |
프론트엔드 개발자 삐롱히의 개발 & 공부 기록 블로그