2014년 6월 1일 일요일

TypeScript 문법 특징 (Basic types, Interfaces, Classes)

JavaScript 문법이나 자세한 내부적인 동작들을 알고 TypeScript 문법을 보면 비교를 해보면서 좋겠지만.. 일단 guide 그대로 보고 정리함.

(원문) http://www.typescriptlang.org/Handbook


[Basic Types]

변수 마다 명시적 type 지정을 한다.

var isDone: boolean = false;

array도 마찬가지이고 generic 선언 형식도 지원한다.

var list:number[] = [1, 2, 3];
var list:Array<number> = [1, 2, 3];

enum type도 지원

enum Color {Red = 1, Green, Blue};
var c: Color = Color.Green;
enum Color {Red = 1, Green, Blue};
var colorName: string = Color[2];

alert(colorName);

Any는 뭐 기본이니 TypeScript에서 배열에 이것저것 넣을 때 유용할 거 같다.

var notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
var list:any[] = [1, true, "free"];

list[1] = 100;

void도 있음.

function warnUser(): void {
    alert("This is my warning message");
}


[Interfaces]

기본적인 interface 문법이다.class 선언하듯 property를 정해서 interface로 선언하고 사용하면 된다.

interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
그리고 interface를 사용하되 property 속성을 '?' keyword를 사용하여 optional로 처리 하여 사용하는 class가 사용하지 않게 할 수 있음.

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

function createSquare(config: SquareConfig): {color: string; area: number} {
  var newSquare = {color: "white", area: 100};
  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

var mySquare = createSquare({color: "black"});
type checking 상황에서 Interface를 사용하게 될 경우 실수로 인한 오타의 경우 (ex config.collor) 확인가능하다. 일반적인 javascript 상황에서는 config의 속성으로 인지하고 null값의 변수로 처리되는것에 반해...


[Function Types]

function을 정의하는데 interface를 사용한다....
좀 이상하긴 한데.. 굳이 보자면 static function 정도로 봐야 할까?

interface SearchFunc {
  (source: string, subString: string): boolean;
}

var mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
  var result = source.search(subString);
  if (result == -1) {
    return false;
  }
  else {
    return true;
  }
}


[Array Types]

Array도 interface를 사용해서 선언 할 수 있음.
다만 index는 number, string만 가능하다.

interface StringArray {
  [index: number]: string;
}

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

이건 뭐...?
interface Dictionary {
  [index: string]: string;
  length: number;    // error, the type of 'length' is not a subtype of the indexer
} 


[Class Types]

Interface를 선언하고 그것을 implements 해서 class를 선언

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

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

class와 interface를 사용할 때 static side, instance side의 개념을 이해해야 함.
아래 예제의 interface에서 선언된 constructor(new)는 static side에서 선언되어 있어 class의 instance side에서 new가 선언되어 있지 않는 것으로 판단되어 에러로 처리된다.

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

class Clock implements ClockInterface  {
    currentTime: Date;
    constructor(h: number, m: number) { }
}
그래서 아래와 같이 interface를 구현하지 말고 class 선언 하고 interface type으로 interface의 instance(엄밀히보면 아닐 듯)를 만들고 다시 instance를 생성자를 호출해서 만들며 초기화 한다..(맞나??)
좀 복잡하다.. 사실 상 interface로 생성자를 명시할 필요는 있겠지만.. 생각보다 복잡하네..

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

class Clock  {
    currentTime: Date;
    constructor(h: number, m: number) { }
}

var cs: ClockStatic = Clock;
var newClock = new cs(7, 30);


[Extending Interfaces]

interface 간 상속이 가능하다. 다중상속도 가능하다.

interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

var square: Square;
square.color = "blue";
square.sideLength = 10;


[Hybrid Types]

위에서 언급함 interface의 속성들이 함께 사용 될 수 있음.

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}

var c: Counter;
c(10);
c.reset();
c.interval = 5.0;

[Classes]

간단한 class 예제

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

var greeter = new Greeter("world");

아래는 컴파일 된 JavaScript 결과물임.

var Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    return Greeter;
})();

var greeter = new Greeter("world");



[Inheritance]

아래에서 sam은 type을 선언하지 않고 바로 Snake의 instance를 생성하고 tom은 Animal type으로 Horse의 instance를 생성했음. 결과적으로는 각 class들(Snake, Horse)에서 override한 move()이 호출 된다.
JavaScript의 속성상 move()의 prototype이 다르더라도 Animal의 move()가 각 class에서 override 되어 tom의 type이 Animal이라도 instance에서 override된 move()가 호출 되었음.

class Animal {
    name:string;
    constructor(theName: string) { this.name = theName; }
    move(meters: number) {
        alert(this.name + " moved " + meters + "m.");
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move() {
        alert("Slithering...");
        super.move(5);
    }
}

class Horse extends Animal {
    constructor(name: string) { super(name); }
    move() {
        alert("Galloping...");
        super.move(45);
    }
}

var sam = new Snake("Sammy the Python");
var tom: Animal = new Horse("Tommy the Palomino");

sam.move();
tom.move(34);


[Private/Public modifiers]

기본적으로 class내의 properties, methods들은 public 속성을 가지고 private으로 선언하고자 할 경우 모든 variables, methods에 명시적으로 private을 선언해야 한다.


[Parameter properties]

private 키워드를 생성자의 argument에 선언하면 실제 class의 property를 argument로 초기화 한다는 얘기, public property도 마찬가지.

class Animal {
    constructor(private name: string) { }
    move(meters: number) {
        alert(this.name + " moved " + meters + "m.");
    }
}

[Accessors]

setter, getter를 위해 set, get 지시자를 사용하면 된다.
다만 compiler output을 ECMAScript 5로 설정해야지 사용 가능하다고 함.

ar passcode = "secret passcode";

class Employee {
    private _fullName: string;

    get fullName(): string {
        return this._fullName;
    }
 
    set fullName(newName: string) {
        if (passcode && passcode == "secret passcode") {
            this._fullName = newName;
        }
        else {
            alert("Error: Unauthorized update of employee!");
        }
    }
}

var employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    alert(employee.fullName);
}

[Static Properties]

static properties도 사용 가능함.. 일반적임.

class Grid {
    static origin = {x: 0, y: 0};
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
        var xDist = (point.x - Grid.origin.x);
        var yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) { }
}

var grid1 = new Grid(1.0);  // 1x scale
var grid2 = new Grid(5.0);  // 5x scale

alert(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
alert(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

[Constructor functions]

좀 이상한 예제긴 하지만 greeterMaker에 Greeter 원형을 대입해서 static property를 수정할 수 있는 것을 볼 수 있음.

class Greeter {
    static standardGreeting = "Hello, there";
    greeting: string;
    greet() {
        if (this.greeting) {
            return "Hello, " + this.greeting;
        }
        else {
            return Greeter.standardGreeting;
        }
    }
}

var greeter1: Greeter;
greeter1 = new Greeter();
alert(greeter1.greet());

var greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
var greeter2:Greeter = new greeterMaker();
alert(greeter2.greet());

[Using a class as an interface]

class도 interface로 사용할 수 있음.. 근대 왜?

class Point {
    x: number;
    y: number;
}

interface Point3d extends Point {
    z: number;
}

var point3d: Point3d = {x: 1, y: 2, z: 3};

댓글 없음:

댓글 쓰기