TypeScript入門帳

# TypeScriptでJavaScriptを作成してみた

## はじめに

> TypeScript はマイクロソフトによって開発され、メンテナンスされているフリーでオープンソースのプログラミング言語である。 TypeScriptはJavaScriptに対して、省略も可能な静的型付けとクラスベースオブジェクト指向を加えた厳密なスーパーセットとなっている。
Wikipediaより

[公式サイト  TypeScript - JavaScript that scales.](http://www.typescriptlang.org/)

## インストール

[node.js](https://nodejs.org/ja/)をインストール

npm でインストール

```bash
$ node -v
v10.15.0

$ npm install -g typescript
```

## TypeScriptファイルを作成してJavascriptにコンパイル

tsファイルでファイルを作成

```main.ts
class User {

}

console.log('hello world');
```

tscコマンドで対象ファイルをコンパイルすると同じ階層にjsファイルが作成される

```bash
tsc main.ts
```

```main.js
var User = (function () {
    function User() {
    }
    return User;
}());
console.log('hello world');
```

## TypeScriptとJavaScritpの型の違い

- TypeScript = 静的型付け言語
- JavaScritp = 動的型付け言語

例

```main.ts
var x: number = 10;
x = 'hello';
console.log(x);
```

```bash
$ tsc main.ts
main.ts:2:1 - error TS2322: Type '"hello"' is not assignable to type 'number'.
// TypeScriptは、xの型がnumberと数字で決められたため、"hello"を代入することができない
```

```main.js
var x = 10;
x = 'hello';
console.log(x);
```

```bash
$ node main.js
hello
// JavaScriptは、xの型がnumberと数字と代入したあとでも、"hello"という文字へ型を変更することが可能
```

## 基本の型

```ts
let 変数名: 型 = 値;
```

公式から
http://www.typescriptlang.org/docs/handbook/basic-types.html


### Boolean 真偽値

```ts
let variable = true; // false
```

### Number 数値

0-9の10進法のほかにも0-Fの16進法や小数点が可能

```let
let number = 10;
let number = 0xf;
let number = 10.1;
```

### String 文字

文字列

### Array 配列

```ts
let list: number[] = [1, 2, 3];
```

### Tuple

型が指定されているものに対して、配列を表現できる

```ts
let x: [string, number]; // 0番目が文字、1番目が数値
x = ['hello', 10]; //　[文字,数値]なのでOK
x = [10, 'hello']; //　[数値, 文字]なのでerror
```

### Enum

列挙型

```ts
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;
let colorName: string = Color[2];
```

### Any

JavaScriptの動的型付け

```number.ts
var x: number = 10;
x = 'hello';
console.log(x);
```

```bash
$ tsc number.ts //error
```

```any.ts
var x: number = 10;
x = 'hello';
console.log(x);
```

```bash
$ tsc any.ts // OK
```

### Void

anyと似ている。関数のreturnの値を決めるときに使用する

```hello.ts
function hello(): void {
  console.log("Hello World !");
}
hello();
```

```bash
$ tsc hello.ts
$ node hello.js
Hello World !
```

### Never

決して起こらない型らしい。never occur.
これは使用するときがいまいちわからなかった。

## 関数の型のオーバーロード

型が複数あった場合

```main.ts
let add = function (a: any, b: any) {
    if (typeof a === 'string' && typeof b === 'string') {
        return a + ' ' + b;
    } else {
        return a + b;
    }
}

console.log(add(4, 6));
console.log(add('Hello', 'World!'));
```

```bash
$ tsc main.ts
$ node main.js
10
Hello World!
```

## クラス

ECMAScript 2015からのclassにも対応している

```user.ts
class User {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    hello(): void {
        console.log('Hello I\'m ' + this.name);
    }
}

let tom = new User('Tom');
console.log(tom.name);
tom.hello();
```

```bash
$ tsc main.ts
$ node main.js
Tom
Hello I'm Tom
```

### アクセス修飾子（public,protected,private）

```user.ts
class User {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    hello(): void {
        console.log('Hello I\'m ' + this.name);
    }
}

let tom = new User('Tom');
console.log(tom.name);
tom.hello();
```

これは厳密にpublicが省略されている

```user.ts
class User {
    public name: string;
    constructor(name: string) {
        this.name = name;
    }
    public hello(): void {
        console.log('Hello I\'m ' + this.name);
    }
}

let tom = new User('Tom');
console.log(tom.name);
tom.hello();
```

privateにするとインスタンスから読みだせなくなる

```user.ts
class User {
    private name: string;
    constructor(name: string) {
        this.name = name;
    }
    private hello(): void {
        console.log('Hello I\'m ' + this.name);
    }
}

let tom = new User('Tom');
console.log(tom.name);
tom.hello();
```

```bash
$ npx tsc main.ts
main.ts:12:17 - error TS2341: Property 'name' is private and only accessible within class 'User'.

12 console.log(tom.name);
                   ~~~~

main.ts:13:5 - error TS2341: Property 'hello' is private and only accessible within class 'User'.

13 tom.hello();
       ~~~~~


Found 2 errors.
// こうなってアクセスできませんとエラーが表示される
```

privateから値を取得、セットするときはgetとsetを使用する

```main.ts
class User {
    private _name: string;
    constructor(_name: string) {
        this._name = _name;
    }
    public hello(): void {
        console.log('Hello I\'m ' + this._name);
    }
    get name() {
        return this._name;
    }
    set name(newValue: string) {
        this._name = newValue;
    }
}

let tom = new User('Tom');
console.log(tom.name);
tom.hello();
```

```bash

$ npx tsc main.ts -t es5 // オプションでes5を指定する
$ node main.js
Tom
Hello I'm Tom
```

## Interface（オブジェクトへ型付け）

オブジェクトの型付け

```main.ts
function getTotal(result: { a: number, b: number }) {
    return result.a + result.b;
}
let result = {
    a: 32,
    b: 58
}
console.log(getTotal(result));
```

```bash
$ npx tsc main.ts -t es6
$ node main.js
90
```

このgetTotalのオブジェクトプロパティを分離する

```main.ts
// 分離にinterfaceを使用する
interface Result {
    a: number,
    b: number
}

function getTotal(result: Result) {
    return result.a + result.b;
}
let result = {
    a: 32,
    b: 58
}
console.log(getTotal(result));
```

```bash
$ npx tsc main.ts -t es6
$ node main.js
90
```

### 継承

```main.ts
// 分離にinterfaceを使用する
interface Result {
    a: number,
    b: number
}

// interfaceはextendsを使い継承することが可能
interface ResultA {
    a: number
}
interface ResultB extends ResultA {
    b: number
}
function getTotal(result: ResultB) {
    return result.a + result.b;
}
```

### classの組み合わせ

```main.ts
interface Test {
    result: number;
    showResult(): void;
}

// implements interface名とすることでclassには必ずinterfaceの値を含んでいないいけないとすることができる
class User implements Test {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    hello(): void {
        console.log('Hello I\m ' + this.name);
    }
}
```

```bash
$ npx tsc main.ts -t es6
main.ts:6:7 - error TS2420: Class 'User' incorrectly implements interface 'Test'.
  Type 'User' is missing the following properties from type 'Test': result, showResult

6 class User implements Test {
        ~~~~


Found 1 error.
```

## 内部モジュール

```main.ts
module UserModule {
    export let name = 'Tom';// 出力する際はexportをつける
    export module ResultModule {
        export let result = '80';
    }
}
console.log('I\'m ' + UserModule.name)
console.log('Result ' + UserModule.ResultModule.result)
```

```bash
$ npx tsc main.ts -t es6
$ node main.js
I'm Tom
Result 80
```

### moduleを別ファイルに分ける

```user.ts
module UserModule {
    export let name = 'Tom';
    export module ResultModule {
        export let result = '80';
    }
}
```

```main.ts
/// <reference path='./user.ts' /> // /が3つと<reference />でmoduleファイルを指定する

console.log('I\'m ' + UserModule.name)
console.log('Result ' + UserModule.ResultModule.result)
```

```bash
tsc main.ts
node main.js
// これだとmain.tsのみがコンパイルされmoduleのファイルがコンパイルされない
```

```bash
tsc main.ts --out all.js // outオプションを付与して別ファイルにmain.tsとuser.tsを合わせたファイルをコンパイルする
node all.js
I'm Tom
Result 80
```

## 外部モジュール

```user.ts
export let name = 'Tom';
```

```result.ts
export let result = '80';
```

```main.ts
import User = require('./user');
import Result = require('./result');

console.log('I\'m ' + User.name)
console.log('Result ' + Result.result)
```

これでmain.tsをコンパイルすればよいが方法はいくつかある
今回はNode - CommonJS

```bash
$ npx tsc main.ts -m commonjs
$ node main.js
I'm Tom
Result 80
```

JSはこんな感じで出力される

```main.js
"use strict";
exports.__esModule = true;
var User = require("./user");
var Result = require("./result");
console.log('I\'m ' + User.name);
console.log('Result ' + Result.result);
```

```user.js
"use strict";
exports.__esModule = true;
exports.name = 'Tom';
```

```result.js
"use strict";
exports.__esModule = true;
exports.result = '80';
```

## 感じたこと

型がきっちりきめないといけなく、それが戸惑うところ
正直jQueryでJavaScriptを数百行単位で開発していたころに比べ、さらにJavaScriptの開発を大掛かりにしてくるととても重要になってくると感じた
JavaScriptを大規模開発で使用を前提として考えているのも納得
（sassが出始めてときもこんな感じだったのかと思う）