원문 : 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 |