원문 : https://www.typescriptlang.org/docs/handbook/interfaces.html
TypeScript의 핵심 원칙(?)은 type-checking 이다. 이를 위해 Interface라는 녀석을 정의해서 사용한다.
간단한 코드를 통해서 알아보자.
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
type-checker는 printLabel 이라는 함수를 체크한다. printLabel 이라는 함수는 labelledObj 라는 매개변수를 받는데 이 녀석은 label 이라는 string 타입의 변수가 반드시 필요한 녀석이다. 매개변수가 다른 값들을 가질 수도 있겠지만 printLabel 이라는 함수를 실행하기 위한 매개변수는 반드시 label 이라는 string 타입 변수를 가지고 있어야 한다.
위 예제를 interface를 사용해서 다시 작성해보자.
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
먼저 예제에서 처럼 string 타입의 label이라는 변수를 가지는 interface(LabelledValue)를 정의하고, printLabel 함수에서 이 interface 타입을 매개변수로 받아 처리한다. 이와같이 interface는 함수와 함수간, 코드와 코드간에 사용하기로하는 값들의 형태에 대한 계약(?) 같은 거라고 생각하면 될듯 하다.
Optional Properties
말 그대로 optional 한 값들을 정의하는 방법이다.
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"});
createSquare 함수는 SquareConfig 모양의 config 를 매개변수로 받아 color(string 타입), area(number 타입) 두변수를 가지는 값을 반환한다.
그런데 config는 color 이라는 값을 가질 수도 있고 안가질 수도 있다. width 라는 값도 가질수도, 안 가질 수도 있다.
이런경우 inteface 정의 부분에 변수명 뒤에 "?"를 붙여 optional 값으로 정의한다.
"어차피 있어도 되고 없어도 되는 값을 정의할거면 뭐하러 inteface를 쓰나?" 라고 생각 할 수도 있다.
하지만 함수내에서 color이라는 값을 colr 이라고 잘못쓴다던가 width를 with라고 잘못쓰는 실수들을 체크 할 수 있다. (그냥 javascript라면 colr, with 라는 신규 변수를 생성 할 것이다.)
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= a; ro[0] = 12; // error! ro.push(5); // error! ro.length = 100; // error! a = ro; // error! //위 코드를 실행하고 싶다면 아래와 같이 작성하면 된다. a = ro as number[];
readonly vs const
변수에는 const를 쓰고 속성 값 에는 readonly를 쓰자
Excess Property Checks
위 예제에서 createSquare 함수는 SquareConfig라는 인터페이스를 매개변수로 받아서 처리하도록 정의했고, SqureConfig 인터페이스는 color, width라는 이름의 값들을 선택적으로 사용하기로 했다. 하지만 어떤이름의 값이 들어올지 미리 알 수 없는 경우에는 어떻게 할까?
//에러난다.
let mySquare3 = createSquare({color: "black", opacity: 0.5});
이런경우 아래와 같이 interface를 고치면 에러 가 사라진다.
//interface를 고쳐보자
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
사실 왜 이렇게 하는지 잘 모르겠다... 왜냐하면...
let tt = {colr: 'black', opacity: 0.5};
let mySquare = createSquare(tt);
let mySquare2 = createSquare({color: "black", opacity: 0.5} as SquareConfig);
위와 같이 처리해도 잘 되기 때문이다.... 이유를 아시는분들은 댓글좀 달아주세요
Function Types
interface 는 function type으로 정의 할 수도 있다.
기본적인 형식은 아래와 같다.
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
//매개변수의 type 만 맞춰주면 된다. 매개변수 명까지 맞출필요는 없다.
//mySearch = function(src: string, subString: string) {
//매개변수의 type 을 안적어도 된다.
//mySearch = function(src, sub) {
mySearch = function(source: string, subString: string) {
let result = src.search(subString);
return result > -1;
}
Indexable Types
interface 는 index를 붙일 수 있는 값도 정의가 가능하다. index는 number 아니면 string이 올 수 있으며, 한 interface 안에 number와 string 두 종류의 index를 모두 가질 수는 없다. (내부적으로 number형 index는 string 형으로 변환되어 처리 되기때문에 number나 string 이나 결국 모두 string 형태로 처리된다.)
interface StringArray {
[index: number]: string;
}
let array1: StringArray;
array1 = ["Bob", "Fred"];
let str: string = array1[0];
interface Dictionary {
[x: string] : string;
}
let array2: Dictionary;
array2['first'] = 'Bob';
array2['second'] = 'Fred';
index를 가지는 값의 형태에 따라 해당 interface에서 기본적으로 갖는 값도 제한된다.
interface NumberDictionary {
[index: string]: number; //여기가 number 형 이기 때문에,
length: number; // number 형이라 괜찮다.
name: string; // string 형이라 안된다. NumberDictionary['name']과 똑같기 때문에 number 형이 되어야 한다.
}
readonly로 읽기 전용 배열을 만들 수 있다.
interface ReadonlyStringArray {
readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error!
Calss Types
- Implementing an inteface
C#이나 Java 같이 interface를 구현하는 class에 대한 정의가 가능하다.
interface ClockInterface { currentTime: Date; setTime(d: Date); } class Clock implements ClockInterface { //interface에 정의된 내용들(currentTime, setTime)이 모두 구현되어야 한다. currentTime: Date; setTime(d: Date) { this.currentTime = d; } constructor(h: number, m: number) { } }
- Difference between the static and instance sides of classes
class 는 static side 와 instance side를 가진다. class 가 interface를 구현할때 instance side 만 체크를 하기 때문에 생성자를 구현하려고 하면 에러가 발생한다.
interface ClockConstructor {
new (hour: number, minute: number);
}
class Clock implements ClockConstructor {
currentTime: Date;
constructor(h: number, m: number) { }
}
에러 내용 : Class 'Clock' incorrectly implements interface 'ClockConstructor'.
Type 'Clock' provides no match for the signature 'new (hour: number, minute: number): any'
이런 경우 생성자를 위한 interface, instance side를 위한 interface를 분리 구현하고 생성자를 실행하는 함수를 정의하여 해결 할 수 있다.
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick();
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
Extending Interfaces
interface 는 서로 상속할 수 있으며, 다중 상속이 가능하다.
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = {};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
Hybrid Types
property를 가지는 함수를 정의 할 수 도 있다.
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
'TypeScript > Handbook' 카테고리의 다른 글
| [TypeScript] 05. Functions (0) | 2018.01.31 |
|---|---|
| [TypeScript] 04. Classes (0) | 2017.07.07 |
| [TypeScript] 02. 변수 선언 (0) | 2017.04.25 |
| [TypeScript] 01. Basic Types (0) | 2017.04.24 |
| [TypeScript] 00. 시작하기 (0) | 2017.04.21 |