TypeScript　まとめ1（型について）

## TypeScriptまとめ集
1. [型について](https://qiita.com/hiro-hiro/items/f2dc671f48e2599e923b)
1. [ユーティリティ型](https://qiita.com/hiro-hiro/items/0f0201a8a49db06e0032)
1. [モジュール化](https://qiita.com/hiro-hiro/items/9879a297cf5b30c474e2)
1. [インストール〜コンパイル方法](https://qiita.com/hiro-hiro/items/52108807c0594798e1c3)

## 目次
1. [TypeScriptとは？](#1typescriptとは) 
1. [型について](#2型について)
1. [関数](#3関数)
1. [高度な型について](#4高度な型について)
1. [インターフェイスを使用した型定義](#5インターフェイスを使用した型定義)

## 1.TypeScriptとは？
* <strong>altJS(JavaScriptの代替言語)</strong>の一種。
* altJSは他にもCoffeScript, Dart, Elm, ClojureScriptなどが存在する。
* TypeScriptはJavaScriptにはできなかった変数、関数の型宣言が可能。
* Angular, React, Vue.jsなどのJavaScriptフレームワークで標準採用されている。

## 2.型について

### 型の種類

**基本となるプリミティブ型**

|型|説明|例|
|---|---|---|
|number|数値型(整数/浮動小数点数)|1, -1, 0.5|
|string|文字列型|'aiueo', "あいうえお"|
|boolean|真偽型(true/false)|true, false|
|symbol|シンボル型|Symbol('あいうえお')|
|null|null|null|
|undefined|undefined|undefined|

**プリミティブ型以外の型**

|型|説明|例|
|---|---|---|
|any|任意の型|1, 'あいうえお', true|
|unknown|型が不明|1, 'あいうえお', true|
|array|配列型|[1, 2, 3]|
|tuple|タプル型(配列型の中でも値の型が統一されていないもの)|[1, 'AAA', true]|
|enum|列挙型|省略(後述の例文で紹介)|
|function|関数|省略(後述の例文で紹介)|
|void|戻り値なし|省略(後述の例文で紹介)|
|never|絶対に起こらない|無限ループ、常にエラーを吐き出すなど|
|それ以外|Date, RegExp, JSON, URLなど|色々|

* 型を省略した場合はany型扱いになる。
* any型とunknown型は似ているが、any型は各型のプロパティが使用できるのに対し、unknown型は使用できない。
* サイトによってプリミティブ型の定義が違っていたりするため、注意（これもあっているか微妙）。

```typescript:typescript(プリミティブ型の型宣言)
//数値型
let num:number = 100;

//文字列型
let str:string = 'qiita';

//真偽型
let bool:boolean = true;

//シンボル型
let sym:symbol = Symbol('qiita'); 

//null
let nul:null = null;

//undefined
let und:undefined = undefined;
```

```typescript:typescript(プリミティブ型以外の型宣言)

//any型(何でも代入できる、ただし基本は使わないこと)
let any1:any = 1;
any1.indexOf(1);  //コンパイルは通るが、実行時エラー

//unknown型
let unknown1:unknown = 1;
unknown1.indexOf(1);  //コンパイルエラー

//配列型
let array1:string[] = ['C#', 'Java', 'Ruby', 'Python'];
let array2:number[][] = [[10,20],[30,40],[50,60]];
let array3:Array<string> = ['C#', 'Java', 'Ruby', 'Python'];
let array4:Array<[number,number]> = [[10,20],[30,40],[50,60]];

//読み取り専用の配列
//ただし、readonlyキーワードがチェックするのは一次元目の要素のみ
let array5: readonly string[] = ['Java','Python','PHP','Ruby'];
let array6: ReadonlyArray<string> = ['Java','Python','PHP','Ruby'];
array3[1] = 'Python 3';    //結果：エラー

//連想配列
let dict1: { [index: string]: string } = {
  'hoge': 'ほげ',
  'foo': 'ふぅ',
  'bar': 'ばぁ',
};

//タプル型　複数の異なる型の集合を表すためのデータ型
let tuple1: [string,number,boolean] = ['hoge',10.355,false];

//列挙型
//値を割り当てない場合は、上から0,1,2と値が振り分けられる
enum enum1{
  MALE = 0,
  FEMALE = 2,
  OTHER = 4
}
let m:enum1 = enum1.OTHER;
console.log(m);         //結果:4
console.log(enum1[m]);  //結果:OTHER

//関数
function func1(arg1:string, arg2?:number=0){
  return `${arg1}の値は${arg2}です。`
}

//void型
function void func2(arg1:string, arg2:number=0){
  console.log('戻り値なし');
}

//never型
function func3(message: string): never {
    throw new Error(message);
}

//それ以外
let date:Date = new Date();
```

### 型変換
* 型変換をしたい場合は、以下の２種類のやり方で変換できる

```typescript:typescript(型変換)
let str:string = '100';

//<型名>　で変換する
let num1:number = <number>str;

//as 型名　で変換する
let num2:number = str as number;
```

## 3.関数
### 関数の宣言方法

関数の宣言方法は

* function
* 関数リテラル
* アロー関数(ラムダ式)
の３種類がある。

```typescript:typescript(関数の宣言方法)
//function
function triangle(base: number, height: number):number{
  return base * height / 2;
}

//関数リテラル(2パターン)
let triangle = function(base: number, height: number): number {
  return base * height / 2;
}
let triangle: (base: number, height: number) => number =
  function (base: number, height: number): number {
    return base * height / 2;
}

//アロー関数（ラムダ式）(2パターン)
let triangle = (base: number, height: number):number => {
  return base * height / 2;
};
let triangle = (base: number, height: number):number => base * height / 2;
```

### 戻り値、引数について
* void, neverの使用方法について
* 引数を省略、既定値、可変調などの設定方法について

```typescript:typescript(色々な関数)

//void　戻り値なし
function VoidFunc(name: string): void{
  console.log(`こんにちは、${name}さん!`);
}

//never　決してありえない（常に例外を出す、無限ループとなっているなど、終点に到達できない）
function NeverFunc(): never {
  throw new Error('Error is occured.');
}

//引数を省略可能
function OmitFunc(name?:string):string{
  if(val === undefined){
    return `こんにちは、名無しさん!`;
  } else {
    return `こんにちは、${name}`さん!;
  }
}

//引数の既定値
function DefaultFunc(name:string='佐藤'):string{
  return `こんにちは、${name}さん!`;
}

//可変調引数
function VariableFunc(...values: number[]){
  let result = 0;
  for(let value of values){
    result += value;
  }
  return result;
}
```
### オーバーロード
* オーバーロード...同じ名前でありながら、引数・戻り値の型が異なる関数を定義すること

```typescript:typescript(オーバーロード)

//シグニチャだけの関数を準備
function show(value: number):void;
function show(value: boolean):void;

//オーバーロードした全ての型に対して有効となるように実装する
function show(value: any): void{
  if(typeof value === 'number') {
    ...
  } else {
    ...
  }
}
```

## 4.高度な型について

### 共用型
「複数の値の中のどれか」を表す型

```typescript:typescript(共用型)
//変数
let data: string | boolean;
data = 'hoge';
data = false;

//関数(戻り値)
function square(value: number): number | boolean {
  if(value < 0){
    return false;
  } else {
    return number + 1;
  }
}

//関数(引数)
function show(value: string | number): void {
  if(typeof value == 'string'){
    console.log('string型です。');
  } else {
    console.log('number型です。');
  }
}
```

### 型チェック
* typeofは、プリミティブ型の判定のみに有効
* instanceofはプリミティブ型以外の判定に有効
* inは、特定のメンバーが存在するかを判定

```typescript:typescript(型チェック)
//typeof演算子
function show(value: string | number): void {
  if(typeof value == 'string'){
    console.log('string型です。');
  } else {
    console.log('number型です。');
  }
}

//instanceof/in演算子
function show(value:Person):void{

  //instanceof演算子を使う場合
  if(value instanceof Person) { ... }

  //in演算子を使う場合
  //valueがnameプロパティを持つか
  if('name' in value) { ... }
}
```

### ユーザー定義の型チェック
* is演算子を使用することでユーザー定義の型チェック関数を自作できる

```typescript:typescript(is演算子)
//戻り値がtrueであれば、引数がstring型であることを示している
function isString(test: any): test is string{
    return typeof test === "string";
}

//isStringを使っての関数
function example(foo: any){
    if(isString(foo)){
        //fooはstringであることがis演算子よりわかる
        //そのためlength(string型のメンバー)に安全にアクセスできる
        console.log(foo.length);  
    }
}
```

### null非許容型
TypeScriptでは、既定で全ての型に対してnull/undefinedを設定可能だが、
tsconfig.json内のstrictNullChecksオプションにtrueを設定することで、
全ての型でnull/undefinedの設定を禁止できる。

```typescript:typescript(null非許容型)
let data1:string = null;       //エラー
let data2:string = undefined;  //エラー

//null/undefinedを設定できるようにしたい場合には...
let data1;string|null = null;            //OK
let data2;string|undefined = undefined;  //OK
```

### ?.演算子
* TypeScript3.7以降で使用可能に。
* 「?.」演算子は、null/undefinedでない時にアクセス可能。

```typescript:typescript(?.演算子)
//通常の場合
let hoge:string | null | undefined;
let result = (hoge === null || hoge === undefined) ? undefined : hoge.trim();

//?.の場合
//null,undefinedでなければ、trimを行う
let result = hoge?.trim();

//連続でも指定可能
hoge?.foo?.bar();
```

### 「??」演算子
* TypeScript3.7以降で使用可能に。
* 「??」演算子は、null/undefinedである時に既定値を返す。

```typescript:typescript(??演算子)
//通常の場合
console.log((hoge !== null && hoge !== undefined) ? hoge : '×');

//??演算子の場合
//null,undefinedの場合に既定値を設定する
console.log(hoge ?? '×');
```

### 型エイリアス
特定の型に対して別名(エイリアス)を設定する。

```typescript:typescript(型エイリアス)
//タプル型にFooTypeという名前を付与
type FooType = [string, number, boolean];

//FooType型の変数dataを定義
let data:FooType = ['abc',100,true];
```

### 文字列リテラル型
特定の文字列(文字列リテラル)をそのまま型として利用できる。

```typescript:typescript(文字列リテラル型)
//Season型を定義
type Season = 'spring'|'summer'|'autumn'|'winter';

//Season型ならコンパイルOK
//Season型以外ならコンパイルエラー となる
function getScene(s:Season){...}
getScene('spring'); //OK
getScene('fall');   //エラー
```

### ジェネリック型
汎用的なクラス/メソッドに対して、特定の型を紐づけるための機能

```typescript:typescript(ジェネリック型)

//ジェネリックを定義
//Tには任意の型を入れることができる
class MyGenerics<T>{
  value!:T;
  getValue():T{
    return this.value;
  }
}

let g = new MyGenerics<string>();
g.value = 'Hoge';
console.log(g.getValue());  //結果：Hoge
```

ちなみに

```typescript:typescript(ジェネリック型)
//複数型引数を指定できる
class MyGenerics<T,R>{...}
//型引数の既定値を設定
class MyGenerics<T=string>{...}
//型引数の制限を設定
class MyGenerics<T extends Hoge>{...}
```

クラスだけでなくメソッドにもつけることが可能

```typescript:typescript(ジェネリック型)
//ジェネリックメソッド
class MyCollection{
  static addAll<T>(data:T[],...values:T[]):T[]{
    return data.concat(values);
  }
}
```

## 5.インターフェイスを使用した型定義
インターフェイスについては、[モジュール化(未作成)]()を参考。

### 基本

```typescript:typescript(インターフェイスによる型定義)
//インターフェイスで型を定義
interface Sample{
  name:string;     //プロパティシグニチャ
  hello():string;  //メソッドシグニチャ
}

//インターフェイスで定義した型の変数
let s:Sample{
  name:'Taro',
  hello(){
    return `こんにちは、Qiitaさん!`;
  }
}
```

※インターフェイスを定義せずに型情報を明示したい場合は、以下のようにできる(**オブジェクト型リテラル**)

```typescript:typescript(オブジェクト型リテラル)
let s:{
  name:string,
  age:number
} = {
  name:'Taro',
  age:20
}
```

### プロパティシグニチャ
* 「?」で省略可能なプロパティを表せる
* 「readonly」で読み取り専用プロパティを表せる

```typescript:typescript(プロパティシグニチャ)
interface Sample{
  readonly name:string;
  age?:number;
}
```

### コールシグニチャ
* 関数型を宣言するためのシグニチャ。
* 「(引数:型, ...):戻り値」の形式で宣言する。

```typescript:typescript(コールシグニチャ)
interface hello {
  (name:string):string
}

let h:hello = function(name:string):string{
  return `こんにちは、Qiitaさん!`;
}
```

### メソッドシグニチャ
* メソッドの型を宣言するためのシグニチャ。
* 「メソッド名(引数:型, ...):戻り値」の形式で宣言する。

```typescript:typescript(メソッドシグニチャ)
interface Sample {
  hello(name:string):string
}

let h:Sample = {
  hello(name:string):string{
  return `こんにちは、Qiitaさん!`;
  }
}
```

### インデックスシグニチャ
* 連想配列で使用すると便利

```typescript:typescript(インデックスシグニチャ)
interface NumberAssoc{
  [index:string]:Number
}

let list:NumberAssoc = {
  'hundred': 100,
  'thousand': 1000
};
```

### コンストラクターシグニチャ
* コンストラクターの型を宣言できる。
* 「new(引数1, ...):クラス名」で定義できる。

```typescript:typescript(コンストラクターシグニチャ)
//インターフェイス
interface Figure{
  new(width:number, height:number):Triangle;
}

//継承先
class Triangle{
  constructor(private width:number,private height:number){}
}
```

### WeakType
* すべてのプロパティが省略可能(~?)である型のこと
* 全て省略した場合、もしくは全て入力した場合のみコンパイルが通るので注意。

```typescript:typescript(WeakType)
//インターフェイス
interface MyOption{
  name?:string;
  timeout?:number;
}

let obj1:MyOptions = { name:'AAA'}; //エラー
let obj2:MyOptions = { }; //OK 
let obj3:MyOptions = { name:'AAA', timeout:10}; //OK
```

### keyof
既存の型から、型情報を切り出すことができる。

```typescript:typescript()
interface Product{
  name:string;
  price:number;
}

type ProductKeys = keyof Product;
//これと同義
type ProductKeys = "name"|"price";
```

### LookupTypes
指定の型から配下のプロパティ型を抜き出すことができる。

```typescript:typescript(LookupTypes)
interface Product{
  name:string;
  price:number;
}

//Product型配下のプロパティ型を取得
type NameType = Product['name'];  //string
type NamePriceType = Product['name'|'price'];  //string|number
type HogeType = Product['hoge']; //エラー
```

### Mapped Types
* 既存の型を変換するための仕組み。
* ユーティリティ型も似た仕組み(TODO:URL)

```typescript:typescript(MappedTypes)
interface Product{
  name:string;
  price:number;
}

//Product型のプロパティセットから順にプロパティ名を取り出し、Kに割り当てなさいという意味
//[K in keyof Product]が該当箇所
type ReadonlyProduct = {
  readonly [K in keyof Product]:Product[K]
;}

//これと同義
type ReadonlyProduct = {
  readonly name:string,
  readonly age:number
};
```

※ちなみに
* 「-?」で「?」を解除できる
* 「-readonly」で「readonly」を解除できる

```typescript:typescript(MappedTypes)
interface ReadonlyProduct{
  readonly name:string;
  readonly price:number;
}

type Product = {
  -readonly [K in keyof Product]:ReadonlyProduct[K]
;}

//これと同義
type Product = {
  name:string,
  age:number
};
```

### ConditionalTypes(条件型)
与えられた条件を満たすかどうかによってX,Yのいずれかの型を選択する
三項演算子と似たような感じ。

```typescript:typescript(ConditionalTypes)

//型Tが型Uに代入できなければnever型を返す
type Intersection<T, U> = T extends U ? T:never;
```