TypeScriptを学んだ(1) ~型、クラス編~

## TypeScriptで用意されている型

- number
- string
- boolean
- any

```ts

var i: number;
var i: number = 10;
var i = 10 // 暗黙の型推論

var x; // any型になる

var nums: number[]; // number型の値の配列
```

- 列挙型

```ts

enum Signal {
  Red,      // 0
  Blue = 3,
  Yellow,   // 4
}

Signal.Blue    // 3
Signal['Blue'] // 3
Signal[3]      // 'Blue'

// なお、複数の同名の列挙型を宣言した場合にはそれらがマージされる。
// ただし、最初のメンバを省略できるのはどちらかだけである。
```


## 関数

```ts

function add(a: number, b?: number): number {  // bはオプション
  if (b){
    return a + b;
  } else {
    return a;
  }
}

// bにデフォルトで10を与えたいなら、
function add(a: number, b: number = 10): number {...}

// 式関数で書くなら、
var add = (a: number, b: number): number => a + b;

```

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

TypeScriptでは、関数のオーバーロードは、インターフェイス(signature)と実装部分を分けて書く。

```ts
function add(a: number, b: number): number;
function add(a: string, b: string): number;

function add(a: any, b: any): any {   // この(any, any)は上書きされるので、(1. 'string')とかだとエラーが出る。
  if (typeof a === "string" && typeof b === "string"){
    return a + ' ' + b
  }
  return a + b
}
```

## クラス

```ts
class User {
  public name: string;         // (アクセス修飾子を書かなくても)デフォルトでpublicになる
  constructor(name: string) {  // functionはいらない
    this.name = name;
  }
}

// これを、constuctorの引数の箇所でインスタンス変数の初期化ができる
class User {
  constructor(public name: string) {
    this.name = name;
  }
}
```


フィールドやメソッドをまとめて、そのクラスの`メンバー`と呼ぶ。

### アクセス修飾子

アクセス修飾子は、`public`, `protected`, `private`の３種類。

以下のように、constructorの引数の変数名の前にアクセス修飾子をつけることによっても、フィールドを設定することができる。(他のメソッドの引数につけることはできない)

```ts

class User {
  // public name: string;         // 不要になった
  constructor(public name: string) {  // アクセス修飾子のpublicをつけた
    this.name = name;
  }
}

```

### getterとsetter

getter/setterを定義すると、(privateのfieldであっても)、ClassName.fieldでアクセス/ClassName.field=xで代入することができるようになる。

getter/setterを使う場合のコンパイルは、ECMAScript 5を使い、`tsx main.tsx -t ES5`とする。

```ts

class User {
  constructor(public name: string) {  // アクセス修飾子のpublicをつけた
    this.name = name;
  }

  get name(){
    return this._name;
  }

  set name(newValue: string) {
    this._name = newValue
  }
}
```

### 継承とprotected

```ts

class AdminUser extends User {
  private _age: number;
  constructor (_name:string, _age:number) {
    super(_name);
    this._age = _age;
  }

  public sayHi(): void {
    console.log("my age: ", + this.age);
    super.sayHi();
  }
}

```

先述のUserクラスを継承するAdminUserクラスを書いた。これだと、子クラスにから親クラスのprivateフィールドである_nameにアクセスすることができない。

これを可能にするためには、_nameフィールドをprotected(`自分のクラス及び、それを継承するクラス内でのみ使えるアクセス修飾子`)にするとよい。変数名も、_nameではなく、nameにしよう。


### static member (= static field + static method)

```tsx

class User {
　static count = 0;     // static fieldの定義
  name: string;
  constructor(name: string) {
    this.name = name;
    User.count++       // static fieldへのアクセス
  }
  static showDescription(): void {  // static methodの定義
    console.log("This class is constructed" + User.count + "times");
  }
}


User.showDescription(); // static methodの実行
```


### インターフェイス

型の組み合わせを変数として保持するもの。(Javaだと、メソッドの引数の型と返り値の型の組み合わせを定義するものだったな)

```tsx

interface Result {
  a: number;
  b: number;
}
```

インターフェイスは、継承することもできるし、変数自体をオプション指定にすることもできる。

```tsx
interface SpringResult {
  spring: number;
}

interface FallResult {
  fall: number;
}

interface FinalResult implements SpringResult, FallResult {
  final?: number;  // spring, fallに加え、c(optional)を持つ
}

function getTotal(result: FinalResult) {
  if (result.final) {
    return result.spring + result.fall + result.final;
  } else {
    return result.spring + result.fall;
  }
}

```


**[1]インターフェイスを関数の引数として使用する場合**

先述の通りである。

**[2]インターフェイスをクラスで実装する場合**

そのクラスは、interfaceで定義されたフィールド/メソッドを持っている必要がある。


```tsx

interface GameUser {
  score: number;
  showScoreWithMemo(memo:string): void;  // methodのsignature
}

class User implements GameUser {
  score:number = 0;   // ちゃんと実装

  constructor(name:string) {
    this.name = name;
  }

  showScoreWithMemo(memo: string): void {  // ちゃんと実装した
    console.log("score: " + this.score);
  }
}

```

### Generics型

**[1]関数で使用する場合**

```tsx

function getArray<T>(value: T) : T[] {
  return [value, value, value]
}

console.log(getArray<string>("a")); // Tにstringが代入される
// ["a", "a", "a"]

// <string>を省略した場合には型推論によって、Tにstringが代入されるっぽい。
console.log(getArray("a")); 
// ["a", "a", "a"]
```

**[2]クラスで使用する場合**

```tsx

class User<T>{...}

// 初期化する際には<T>も書く
u = new User<T>();

// <T>を省略した場合には型推論が使われる
u = new User();
```


**Generics型に制約を与える**

Generic型に対して、それが`特定のフィールドを持つオブジェクトである`という制約を与えたい場合には、`T extends (interface)`としてやれば良い。なお、`Tがinterfaceで定義されるプロパティを指定された形でもつ`ことが最低条件になる。前述したように、これは、最低条件であり、これに加えてcというattributeを持っていたりしても構わない。


```tsx

interface Result {
  a: number;
  b: number;
}

class MyData<T extends Result> {   // Tはa(number)とb(number)というattributeを持つことがmust
  constructor(public value: T) {}

  getArray(): T[] {
    return [this.value.a, this.value.b]
  }
}

var number_values = new MyData<Result>({a: 1, b: 2});
console.log(number_values.getArray());

// <Result>を省略した場合、T = Resultになるっぽい
var number_values = new MyData({a: 1, b: 2});
console.log(number_values.getArray());

```




## 参考

- [ドットインストール TypeScript入門 #01~#17](https://dotinstall.com/lessons/basic_typescript)