Interface


1) 첫번째 인터페이스 (Our First Interface)

/* 1. 인터페이스 사용 X */
function printLabel(labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj); 
// OK. 필요한 속성이 '최소한' 있고 타입이 맞는지 체크.

/* 2. 인터페이스 사용 O */
interface LabelledValue {
  label: string;
}
function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label); // Error!
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

2) 선택적 프로퍼티 (Optional Properties)

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;
    // newSquare.color = config.clor; // 오류!
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

let mySquare = createSquare({ color: "black" });


3) 읽기 전용 프로퍼티 (Readonly properties)

interface Point {
  readonly x: number; // 읽기 전용 프로퍼티
  readonly y: number;
}

let p1: Point = { x: 10, y: 20 };
p1.x = 5; // 오류!

ReadonlyArray 타입

let a: number[] = [1, 2, 3, 4];
let ra: ReadonlyArray<number> = a;

ra[0] = 12; // 오류!
ra.push(5); // 오류!
ra.length = 100; // 오류!

a = ra; // 오류! => 일반 배열에 다시 할당하는 것도 불가능

a = ra as number[]; // 가능! => 타입 단언(assertion)을 통해 오버라이드

readonly vs const


4) 프로퍼티 초과 체크 (Excess Property Checks)

interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
  // ...
}

let mySquare = createSquare({ colour: "red", width: 100 });
// 오류 : 'colour'는 'SquareConfig' 타입에서 필요하지 않습니다.

프로퍼티 초과 체크를 피하는 3가지 방법

let mySquare2 = createSquare({ borderColor: "red", width: 100 } as SquareConfig);
interface SquareConfig { 
	color?: string; 
	width?: number; 
	[propName: string]: any; 
}
interface SquareConfig { 
	color?: string; 
	width?: number;  
}

let squareOptions = { colour: "red", width: 100 }; 
// quareOptions가 추가 프로퍼티 검사를 받지 않기 때문에, 에러X
let mySquare = createSquare(squareOptions);

위의 예제는 squareOptionsSquareConfig 사이에 공통 프로퍼티(width)가 있기 때문에 에러 발생하지 않은 것.

let squareOptions = { colour: "red" };
let mySquare = createSquare(squareOptions); // Error!

위처럼 간단한 코드의 경우, 이 검사를 "피하는" 방법을 시도하지 않는 것이 좋습니다. 메서드가 있고 상태를 가지는 등 더 복잡한 객체 리터럴에서 이 방법을 생각해볼 수 있습니다. 하지만 초과 프로퍼티 에러의 대부분은 실제 버그입니다. 그 말은, 만약 옵션 백 같은 곳에서 초과 프로퍼티 검사 문제가 발생하면, 타입 정의를 수정해야 할 필요가 있습니다. 예를 들어, 만약 createSquare에 color나 colour 모두 전달해도 괜찮다면, squareConfig가 이를 반영하도록 정의를 수정해야 합니다.


5) 함수 타입 (Function Types)

interface SearchFunc {
    (source: string, subString: string): boolean; // 호출 시그니쳐(call signature)
}
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean { // 매개변수 이름은 같을 필요 없음
    let result = src.search(sub);
    return result > -1;
}
let mySearch: SearchFunc;
mySearch = function(src, sub) { // 매개변수 타입 지정 안해줘도 contextual typing으로 추론
    let result = src.search(sub);
    return result > -1;
}


6) 인덱싱 가능 타입(Indexable Types)

interface StringArray {
  [index: number]: string; // 인덱스 시그니처(index signature)
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0]; // Bob

인덱싱 타입 혼합 (문자열 + 숫자)

class Animal {
    name: string;
}
class Dog extends Animal {
    breed: string;
}

interface NotOkay { 
    [stringIndex: string]: Dog;
    [numberIndex: number]: Animal;
}

읽기전용 Index signature

interface ReadonlyStringArray {
    readonly [index: number]: string;
}

let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // 오류!


7) 클래스 타입 (Class Types)

interface ClockInterface {
  currentTime: Date;
  setTime(d: Date);
}

class Clock implements ClockInterface {
  currentTime: Date;
  setTime(d: Date) {
    this.currentTime = d;
  }
  constructor(h: number, m: number) {}
}

클래스의 Static과 Instance의 차이점

https://stackoverflow.com/questions/58399613/what-is-exactly-the-static-side-and-the-instance-side-in-typescript

interface ClockConstructor {
    new (hour: number, minute: number); // Construct signature
}

// Error! 인터페이스를 Implements할 때 클래스의 Instance적인 면만 검사.
class Clock implements ClockConstructor {
    currentTime: Date;
    constructor(h: number, m: number) { }
}

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);

interface ClockConstructor {
  new (hour: number, minute: number);
}

interface ClockInterface {
  tick();
}

const Clock: ClockConstructor = class Clock implements ClockInterface {
  constructor(h: number, m: number) {}
  tick() {
      console.log("beep beep");
  }
}


8) 인터페이스 확장 (Extending Interfaces)

interface Shape {
  color: string;
}

interface PenStroke {
  penWidth: number;
}

// 여러 인터페이스 확장
interface Square extends Shape, PenStroke {
  sideLength: number;
}

// let square = <Square>{};
let square = {} as Square;
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;


9) 하이브리드 타입 (Hybrid Types)

interface jQueryElement {
  // ...
}

interface jQueryInterface {
  (string: query): jQueryElement;
  each: Function;
  ajax: Function;
  // ...
}

const listItem = $('.list-item')
$.each(listItem, (index, item) => {...})


10) 클래스 확장 인터페이스

class Control {
  private state: any;
}

// 클래스를 상속받은 인터페이스
interface SelectableControl extends Control {
  select(): void;
}

class Button extends Control implements SelectableControl {
  select() {}
}

class TextBox extends Control {
  select() {}
}

// 오류: 'Image' 타입의 'state' 프로퍼티가 없습니다.
class Image implements SelectableControl {
	private state: any;
  select() {}
}

class Location {

}