https://www.typescriptlang.org/docs/handbook/interfaces.html
TypeScript는 structural subtyping을 사용한다. 즉, 변수가 가진 모양을 기준으로 타입을 판단한다. interface는 이런 type들의 이름을 지정하는 역할을 한다.
interface LabeledValue {
label: string;
}
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
interface는 LabeledValue라는 타입의 구조(shape)을 정의한다.
printLabel은 myObj가 label이라는 이름을 가진 string 타입을 가진 property가 있는지만 확인하고 그 외는 확인하지 않는다.
property의 이름 뒤에 물음표(?)를 붙여서 존재할 수도 있고 아닐 수도 있는 property임을 명시한다.
이 경우 undefined가 자동으로 포함되기 때문에 undefind를 굳이 별도로 추가하지 않아도 된다.
interface SquareConfig {
color?: string; // color?: string | undefined와 동일
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;
}
property 이름 앞에 "readonly"를 적으면 해당 property는 변경할 수 없게 된다.
readonly와 const는 같은 효과를 갖지만 readonly는 property에, const는 변수에 사용한다.
interface Point {
readonly x: number;
readonly y: number;
}
TypeScript는 객체(object literal)를 새로 만들어 변수에 할당하거나 인자로 넘겨줄 때 excess property checking을 진행한다. 이 과정에서 만약 객체가 지정된 타입에 해당하지 않는 property를 가지고 있다면 에러가 발생한다.
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
...
}
let square1 = createSquare({ clor: "red", width: 100 }); // error!
let square2 : SquareConfig = { clor: "red", width: 100 }; // error!
interface로 함수의 타입을 지정할 수도 있다. 이 때에는 interface에 call signature라는 것을 준다. call signature는 함수의 매개 변수들과 return 타입을 지정한다. 매개 변수들은 모두 이름과 타입을 지정해주어야 한다.
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function (src: string, sub: string) {
let result = source.search(subString);
return result > -1;
};
interface로 a[10]이나 a["daniel"]같은 인덱스 타입도 지정할 수 있다. 이 때는 index signature라는 것을 주는데, 이는 index와 return type을 지정한다.
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
index signature는 string과 number의 두 가지 형태가 가능하다. 둘 다 사용할 수도 있지만 numeric indexer의 return 타입이 string indexer의 return 타입의 subtype이어야 한다. 따라서 아래의 경우는 에러가 발생한다.
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// Error: indexing with a numeric string might get you a completely separate type of Animal!
interface NotOkay {
[x: number]: Animal;
[x: string]: Dog;
}
다른 언어에서와 마찬가지로 interface로 class를 구현할 수 있다. interface에 명시된 method도 class에서 구현할 수 있다. 그리고 interface로 구현한 내용은 class에서 public이다.
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) {}
}
class는 두 가지 타입을 가진다.
class가 interface를 구현할 때에는 instance side만 가능하다.
interface도 class처럼 서로 상속할 수 있고, 다중상속도 가능하다.
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
하나의 객체가 여러 타입을 가지는 경우도 있다.
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); // function type
c.reset(); // object type
c.interval = 5.0; // object type
interface가 class를 상속하게 되면 class의 멤버들만 상속받고 구현은 상속받지 않는다. 또한 base class의 private나 protected 멤버들도 상속받을 수 있기 때문에 이렇게 된다면 이 interface는 base class나 bace class의 상위 클래스로만 구현이 가능하다.