【TypeScript】TypeScriptの雰囲気を掴もう

## はじめに
ざっくり静的型付け言語とES6よりも拡張されたクラスベースを理解することが目的。
TypeScriptを知らない人、静的型付け言語を使ったことがない人、Java等のオブジェクト指向言語を使ったことがない人向け。
自分自身も最近はじめたため誤りがあれば是非教えてくださいmm

## TypeScriptの特徴
- 静的な型システム
- クラスベースのオブジェクト指向の拡張


## 参考
- [公式ドキュメント](http://www.typescriptlang.org/docs/tutorial.html)
- [速習TypeScript](https://www.amazon.co.jp/%E9%80%9F%E7%BF%92TypeScript-altJS%E3%81%AE%E3%83%87%E3%83%95%E3%82%A1%E3%82%AF%E3%83%88%E3%82%B9%E3%82%BF%E3%83%B3%E3%83%80%E3%83%BC%E3%83%88%E3%82%92%E7%B4%A0%E6%97%A9%E3%81%8F%E5%AD%A6%E3%81%B6%EF%BC%81-%E9%80%9F%E7%BF%92%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E5%B1%B1%E7%94%B0%E7%A5%A5%E5%AF%9B-ebook/dp/B0733113NK?SubscriptionId=AKIAIZUJ5XRJDA7WTD3Q&tag=afakejoker-22&linkCode=xm2&camp=2025&creative=165953&creativeASIN=B0733113NK)
- [TypeScript Deep Dive](https://typescript-jp.gitbook.io/deep-dive/)
- [Playground](https://www.typescriptlang.org/play/)
  - ブラウザで手軽にTypeScriptを実行できる
  - コンパイルされたJavaScriptも見れるので学習じにはおすすめ
  - 「ts playground」とかで検索するとアダルトサイトが引っかかってしまうので注意


## TypeScriptの実行
TypeScriptをインストール

```.bash
npm install -g typescript
```

hello.tsを作成

```.hello.ts
function greeter(person: string) {
    return "Hello, " + person;
}

geeter('taro')
```

コンパイル

```.bash
tsc hello.ts
```

hello.jsがつくられる

```hello.js
function greeter(person) {
    return "Hello, " + person;
}
greeter('taro');
```

コンパイルが失敗するようにhello.tsを修正

```.hello.ts
function greeter(person: string) {
    return "Hello, " + person;
}

geeter(10)
```

エラーになる

```.bash
hello.ts:5:9 - error TS2345: Argument of type '10' is not assignable to parameter of type 'string'.

5 greeter(10)
          ~~


Found 1 error.

```

## データ型

### 基本
TypeScriptの静的型（Type）システムにおけるデータ型
- number: 数値型
- string: 文字列型
- boolean: 真偽型
- symbol: シンボル型
- any: 任意の型
- object型: 配列型、タプル型、クラス型、インターフェイス型、関数型

```.ts
let data: string = 'hoge'
data = 'fuga'
data = 100 // エラー
```

### 型推論
型を省略するこも可能

変数宣言で型が省略された場合、初期値から型推論が行われる

```.ts
let data = 100 // 初期値がnumberなので型はnumberだとみなされる
data = 200
data = 'hoge' // エラー

let data2 = 'hoge'
data2 = 'fuga'
data2 = 100 // エラー

let data3 // 初期値を省略すると型はanyとみなされる
data3 = 'fuga'
data3 = 100 //エラーにならない

let data4: any = 100
data4 = 150
data4 = 'hoge' // エラーにならない

let data5 = undefined // 初期値はundefined/nullを指定した場合もany型とみなされる
data5 = 100
data5 = 'hoge' // エラーにならない
```

### 型アサーション
互換性のある型であれば、型を明示的に変換できる

```.ts
function hello(name: string) {
    return `${name}さん、こんにちは！`
}

// 数値型を渡すとエラーになる
hello(100)

// any型に変換
hello(<any>100)

// as構文でも変換可能
hello(100 as any)
```

### 配列
```.ts
// 基本
let data: string[] = ['apple', 'orange', 'peach']
console.log(data[0]) // apple

// ジェネリック記法
let data2: Array<string> = ['apple', 'oarange', 'peach']
```

### 連想配列
```.ts
// indexの部分は何でも良い
let obj: { [index: string]: string } = {
    'apple': '青森',
    'orange': '愛媛',
    'peach': '群馬'
}
```

* 連想配列の注意点

プロパティ名がStringの時に限りドット記法によるアクセスが可能だが、その場合は最初に定義しておく必要がある。

```.ts
let obj = {
    'apple': '青森',
    'orange': '愛媛',
    'peach': '群馬'
}
obj.grape = '山梨' //エラー

// Property 'grape' does not exist on type '{ 'apple': string; 'orange': string; 'peach': string; }'.

obj['grape'] = '山梨' // ブラケット記法ならプロパティの追加が可能
```

### 列挙型
列挙型はユニークな値の有限集合。重複はエラーになる。

```.ts
enum Fruit {
    Apple,
    Orange,
    Peach
}

let f: Fruit = Fruit.Apple
console.log(f) // 0
console.log(Fruit[f]) // Apple
```

### タプル型
異なる型の集合

```.ts
let data: [string, number, boolean] = [ 'hoge', 100, true]
```

型エイリアスをつくることが出来る

```.ts
type SmapleType = [string, number, string]

let data: SampleType = ['hoge', 100, 'fuga']
```

### ユニオン型

```.ts
let data: string | boolean
data = 'hoge'
data = false
dat = 100 // エラー

let data2: (string | number)[] = ['hoge', 100, 100]
```

### 文字列リテラル型

```.ts
type Fruit = 'apple' | 'orange' | 'peach'

let data: Fruit = 'apple'
data = 'grape' // エラー
```

## 関数

### 基本

```.ts
function hello(name: string): string {
    return `${name}さん、こんにちは！`
}

hello('tom') //tomさん、こんにちは！

let hello2 = (name: string): string => {
    return `${name}さん、こんにちは！`
}

hello2('tom') //tomさん、こんにちは！
```

返り値がない場合はvoidを指定

```.ts
function hello(name: string): void {
    console.log(name)
}
```

### 引数の省略

```.ts
function hello(name?: string): string {
    return name === undefined ? 'スルー' : `${name}さん、こんにちは！`
}

hello() //スルー
```

### 引数のデフォルト値

```.ts
function hello(name: string　= 'tom'): string {
    return `${name}さん、こんにちは！`
}

hello() //tomさん、こんにちは！
```

### 可変長引数

```.ts
function hello(...names: string[]) {
    let greet = 'こんにちは！'
    names.forEach(name => {
        greet = `${greet}、${name}さん`
    })
    return greet
}

alert(hello('tom', 'risa')) //こんにちは！、tomさん、risaさん
```

## オブジェクト指向構文

ES6によりクラスベースのオブジェクト指向が登場したが、TypeScriptはそれをさらに拡張する。
具体的には以下のような機能を提供する。
- アクセス修飾子
- インターフェイス
- ジェネリック

### 基本的なクラス
```.ts
class Person {
    name: string
    job: string

    constructor(name: string, job: string) {
        this.name = name
        this.job = job
    }

    introduce(): string {
        return `${this.name}は${this.job}です`
    }
}

let tom = new Person('tom', 'エンジニア')
console.log(tom.introduce()) //tomはエンジニアです
let john = new Person('john', 10) // エラー
```

### アクセス修飾子

- public クラスの外からアクセス可能（デフォルト）
- protected 同じクラス、またはその派生クラスからアクセス可能
- private 同じクラスからのみアクセス可能

```.ts
class Person {
    private name: string
    private job: string

    constructor(name: string, job: string) {
        this.name = name
        this.job = job
    }

    public introduce(): string {
        return `${this.name}は${this.job}です`
    }
}

let tom = new Person('tom', 'エンジニア')
console.log(tom.introduce()) //tomはエンジニアです
console.log(tom.name) //エラー

//Property 'name' is private and only accessible within class 'Person'.
```

TypeScriptでは以下のようにコンストラクターを書くことも可能

```.ts
class Person {
    constructor(private name: string, private job: string) {
        this.name = name
        this.job = job
    }

    public introduce(): string {
        return `${this.name}は${this.job}です`
    }
}
```

### getter / setter

```.ts
class Person {
    private _age: number

    get age(): number {
        return this._age
    }

    set age(value: number) {
        if (value < 0) {
            throw new RangeError('ageプロパティは整数で指定してください')
        }
        this._age = value
    }
}
let p = new Person()
p.age = 10
console.log(p.age)
```

getterはプロパティの参照時に呼び出される
setterはプロパティの書き込み時に呼び出される

### static修飾子

```.ts
class Figure {
    public static Pi: number = 3.14

    public static circle(radius: number): number {
        // 静的メンバーにアクセスする時はthisを明示する
        return radius * radius * this.Pi
    }
}
console.log(Figure.Pi) //3.14
console.log(Figure.circle(2)) //12.56
```

### 名前空間

```.ts
namespace MainApp {
    export class Hoge {
        //...
    }
}
namespace MainApp2 {
    export class Hoge {
        //...
    }
}
let app = new MainApp.Hoge()
```

namespaceは入れ子にすることもできる

### 継承

```.ts
// 継承元のクラス（スーパークラス/親クラス/基底クラス）
class Person {
    protected name: string
    protected sex: string

    constructor(name: string, sex: string) {
        this.name = name
        this.sex = sex
    }

    show(): string {
        return `${this.name}は${this.sex}です`
    }
}

// 継承して出来たクラス（サブクラス/小クラス/派生クラス）
class Engineer extends Person {
    work(): string {
        return `${this.name}はプログラミングします`
    }
}

let engineer = new Engineer('tom', '男')
console.log(engineer.show()) //tomは男です
console.log(engineer.work()) //tomはプログラミングします
```

### オーバーライド

```.ts
class Person {
    protected name: string
    protected sex: string

    constructor(name: string, sex: string) {
        this.name = name
        this.sex = sex
    }

    show(): string {
        return `${this.name}は${this.sex}です。`
    }
}

class Engineer extends Person {
    protected lang: string

    constructor(name: string, sex: string, lang: string) {
        super(name, sex)
        this.lang = lang
    }

    //showメソッドをオーバーライド
    show(): string {
        return super.show() + `好きな言語は${this.lang}です。`
    }
}

let engineer = new Engineer('tom', '男', 'TypeScript')
console.log(engineer.show()) //tomは男です。好きな言語はTypeScriptです。
```

### 抽象メソッド

オーバーライドは派生クラスで基底クラスの上書きを強制しない
抽象メソッドは派生クラスで基底クラスの上書きを強制する

```.ts
//抽象クラスを宣言
abstract class Figure {
    constructor(protected width: number, protected height: number) {}
    //abstract修飾子はコードブロックを持たず最後に「;」で区切る
    abstract getArea() :number;
}

class Triangle extends Figure {
    //抽象メソッドをオーバーライドが必須
    getArea(): number {
        return this.width * this.height / 2
    }
}

let triangle = new Triangle(10, 5)
console.log(triangle.getArea()) //25
```

### インターフェイス

TypeScriptは一度に一つのクラスしか継承できない（単一継承）
インターフェイスは複数のインターフェイスを同時に継承できる（インターフェイスを継承することを実装すると言う）
インターフェイスは全てのメソッドが抽象メソッドである特別なクラス
派生クラスのことを実装クラスと言う

```.ts
//インターフェイスを宣言
interface Figure {
    getArea(): number;
}

class Triangle implements Figure {
    constructor(private width: number, protected height: number) {}
    getArea() :number {
        return this.width * this.heigth / 2
    }
}

let triangle = new Triangle(100, 6)
console.log(t.getArea()) //300
```

* インターフェイスの制限
- メソッドは全て抽象メソッド。抽象メソッドであることは自明なのでabstract修飾子は指定しない
- 全てのメンバーはpublicであることが自明なのでアクセス修飾子は指定しない
- staticメンバーも宣言できない

* インターフェイスを継承してインターフェイスを宣言する

インターフェイスの継承にはimplementsではなくextendsを使う

```.ts
interface Hoge extends Foo, Bar {}
```

### ジェネリック

汎用的なクラス、メソッドに対して特定の型を紐付けるための機能

```.ts
class MyGenerics<Ge> {
    value: Ge
    getValue(): Ge {
        return this.value
    }
}
let ge = new MyGenerics<string>()
ge.value = 'generic'
console.log(ge.getValue) //generic
```