TypeScript 基本構文 忘備録

# 目次
[1. 概要](#1-概要)
[2. 想定読者](#2-想定読者)
[3. 型について](#3-型について)
[4. インターフェース](#4-インターフェース)
[5. 関数](#5-関数)
[6. クラス](#6-クラス)
[7. ジェネリック](#7-ジェネリック)

# 【1. 概要】
TypeScriptをこれから触ってみる方向けに、どんな機能があるのかを簡単にまとめてみました。

# 【2. 想定読者】
・JavaScriptの経験がある方。
・TypeScriptってそもそも何ぞや、の基本は抑えておられる方。

# 【3. 型について】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694199/5a7a155f-e8cb-1165-b9ac-0323d63ecaec.png)

## プリミティブ型
```ts:boolean
const b: boolean = true;
```

```ts:number
const n: number = 0;
```

```ts:string
const s: string = "string";
```

```ts:enum
enum Employee {
    FirstName,
    LastName,
    Age
}

const employeeFirstName: Employee = Employee.FirstName
console.log(employeeFirstName);
// output: 0

console.log(Employee[employeeFirstName]);
// output: FirstNam
```

```ts:any
let randomValue: any = 10;
randomValue = 'Mateo';   // OK
randomValue = true;      // OK
```

## 型アサーション
```(randomValue as string).toUpperCase();```のように```as```構文で「信頼してください、わかってやっています」ということをコンパイラに伝える。
```ts:型アサーション
let randomValue: unknown = 10;

randomValue = true;
randomValue = 'Mateo';

// このまま下記コードを実行すると、'randomValue' is of type 'unknown'.とエラーが出る。
// randomValue.toUpperCase();

if (typeof randomValue === "string") {
    console.log((randomValue as string).toUpperCase());    //* Returns MATEO to the console.
} else {
    console.log("Error - A string was expected here.");    //* Returns an error message.
}
```

## 共用体型
パイプ (```|```) を使用して、代入できる値の型を制限する。
```ts:共用体型
let multiType: number | boolean;
multiType = 20;         // OK
multiType = true;       // OK
multiType = "twenty";   // ERROR
```
## 型ガード
型ガードとは、ある値に対して特定の型かどうかをチェックし、その結果に応じて処理を分けることを指す。
```ts:型ガード
function add(x: number | string, y: number | string) {
    if (typeof x === 'number' && typeof y === 'number') {
        return x + y;
    }
    if (typeof x === 'string' && typeof y === 'string') {
        return x.concat(y);
    }
    throw new Error('Parameters must be numbers or strings');
}

console.log(add('one', 'two'));  //* Returns "onetwo"
console.log(add(1, 2));          //* Returns 3
console.log(add('one', 2));      //* Returns error
```

## 交差型
次の例では、Employee と Manager という 2 つのインターフェイスを定義した後、両方のインターフェイスのプロパティを併せ持つ ManagementEmployee という新しい交差型を作成する。
```ts:交差型
interface Employee {
  employeeID: number;
  age: number;
}
interface Manager {
  stockPlan: boolean;
}
type ManagementEmployee = Employee & Manager;
let newManager: ManagementEmployee = {
    employeeID: 12345,
    age: 34,
    stockPlan: true
};
```

## リテラル型の定義
変数に代入できる値を制限する。
```string```、```number```、```boolean```の3つで使用可能。
```ts:リテラル型の定義
type testResult = "pass" | "fail" | "incomplete";
let myResult: testResult;
myResult = "incomplete";    // OK
myResult = "pass";          // OK
myResult = "failure";       // ERROR
```

## 配列
下記の2通りの定義が可能。
```ts:配列
let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
```

# 【4.インターフェース】
:::note alert
TypeScript のコーディングのガイドラインでは、インターフェイスを文字```I```で始めないことを勧めています。
:::
```ts:インターフェース
interface Employee {
    firstName: string;
    lastName: string;
    age?: number;
    fullName(): string;
}

// Employeeインターフェースの拡張
interface Manager extends Employee {
    phoneNumber: number;
}

let employee: Manager = {
    firstName : "Emil",
    lastName: "Andersson",
    phoneNumber: 12345678901,
    fullName(): string {
        return this.firstName + " " + this.lastName;
    }
}

employee.firstName = 10;  //* Error - Type 'number' is not assignable to type 'string'
```

|プロパティの種類|例|
|---|---|
|必須|firstName: string;|
|省略可能|firstName?: string;|
|読取専用|readonly firstName: string;|


### インターフェイスを使用してJavaScript APIを記述
```ts
const fetchURL = 'https://jsonplaceholder.typicode.com/posts'

interface Post {
    userId: number;
    id: number;
    title: string;
    body: string;
}

async function fetchPosts(url: string) {
    let response = await fetch(url);
    let body = await response.json();
    return body as Post[];
}

async function showPost() {
    let posts = await fetchPosts(fetchURL);
    let post = posts[0];
    console.log('Post #' + post.id)
    console.log('Author: ' + (post.userId === 1 ? "Administrator" : post.userId.toString()))
    console.log('Title: ' + post.title)
    console.log('Body: ' + post.body)
}

showPost();
```

# 【5. 関数】
## 名前付き関数
```ts:名前付き関数
// 戻り値あり
function addNumbers1 (x: number, y: number): number {
   return x + y;
}
addNumbers(1, 2);

// 戻り値なし
function addNumbers2 (x: number, y: number): void {
   console.log(x + y);
}
```

## 匿名関数
```ts:匿名関数
let addNumbers = function (x: number, y: number): number {
   return x + y;
}
addNumbers(1, 2);
```

## アロー関数
```ts:アロー関数
// 匿名関数
let addNumbers1 = function (x: number, y: number): number {
   return x + y;
}

// アロー関数(単一行ver)
let addNumbers2 = (x: number, y: number): number => x + y;

// アロー関数(複数行ver)
let total2 = (input: number[]): number => {
    let total: number =  0;
    for(let i = 0; i < input.length; i++) {
        if(isNaN(input[i])) {
            continue;
        }
        total += Number(input[i]);
    }
    return total;
}
```

## パラメーターの種類
```ts:省略可能パラメーター
function addNumbers (x: number, y?: number): number {
    if (y === undefined) {
        return x;
    } else {
        return x + y;
    }
}

addNumbers(1, 2); // Returns 3
addNumbers(1);    // Returns 1
```

```ts:規定値を指定したパラメーター
function addNumbers (x: number, y = 25): number {
   return x + y;
}

addNumbers(1, 2);  // Returns 3
addNumbers(1);     // Returns 26
```

```ts:restパラメーター
// 複数のパラメーターを 1 つのグループとして扱う
let addAllNumbers = (firstNumber: number, ...restOfNumbers: number[]): number => {
   let total: number =  firstNumber;
   for(let counter = 0; counter < restOfNumbers.length; counter++) {
      if(isNaN(restOfNumbers[counter])){
         continue;
      }
      total += Number(restOfNumbers[counter]);
   }
   return total;
}

addAllNumbers(1, 2, 3, 4, 5, 6, 7);  // returns 28
addAllNumbers(2);                    // returns 2
addAllNumbers(2, 3, "three");        // flags error due to data type at design time, returns 5
```

```ts:分解オブジェクトパラメーター
interface Message {
   text: string;
   sender: string;
}

function displayMessage({text, sender}: Message) {
    console.log(`Message from ${sender}: ${text}`);
}

displayMessage({sender: 'Christopher', text: 'hello, world'});
```

## 関数型
```ts:関数型
// 関数型を定義
type calculator = (x: number, y: number) => number;

// インターフェースの場合は、下記のように定義
interface Calculator {
    (x: number, y: number): number;
}

// 関数型を使用し、関数を定義
let addNumbers: calculator = (x: number, y: number): number => x + y;
let subtractNumbers: calculator = (x: number, y: number): number => x - y;

// 戻り値に関数型を指定
let doCalculation = (operation: 'add' | 'subtract'): calculator => {
    if (operation === 'add') {
        return addNumbers;
    } else {
        return subtractNumbers;
    }
}

let calculationFunction = doCalculation('add');
calculationFunction(1,2); // return 3
```

# 【6. クラス】

## アクセス修飾子
|アクセス修飾子|説明|
|---|---|
|public|アクセス修飾子を指定しない場合、既定値は```public```です。```public```キーワードを使用して、メンバーを明示的に```public```に設定することもできます。|
|private|```private```キーワードを使用してメンバーを変更すると、そのメンバーを含むクラスの外部からはアクセスできなくなります。|
|protected|```protected```修飾子は```private```修飾子とほとんど同じように機能しますが、```protected```と宣言されたメンバーには、派生クラス内でもアクセスできる点が異なります。|
|readonly|```readonly```修飾子を使用して、プロパティを readonly にすることができます。```readonly```プロパティは、宣言時または```constructor```で初期化されるときにのみ設定できます。|

 ```ts:クラス
 // インターフェース
 interface Vehicle {
    // pubclicメンバーのみ宣言可能
    doors: number;
    accelerate(speed: number): void;
}

// Carクラス
class Car implements Vehicle {
    // プロパティ
    private static numberOfCars: number = 0; // staticプロパティ
    private _doors: number;

    // コンストラクタ
    constructor(doors = 4) {
        Car.numberOfCars++;

        if ((doors % 2) === 0) {
            this._doors = doors;
        } else {
            throw new Error('Doors must be an even number');
        }
    }

    // アクセサー
    get doors() {
        return this._doors;
    }
    set doors(doors) {
        if ((doors % 2) === 0) {
            this._doors = doors;
        } else {
            throw new Error('Doors must be an even number');
        }
    }

    // メソッド
    static getNumberOfCars(): number {
        return Car.numberOfCars;
    }
    accelerate(speed: number): void {
        this.showDoor()
        console.log('run accelerate.')
    }
    // サブクラスからもアクセスするため「protected」を使用
    protected showDoor(): void {
        console.log(`ドア数は${this.doors}です。`);
    }
}

// Carクラスを継承したElectricCarクラス
class ElectricCar extends Car {
    // Properties unique to ElectricCar
    private _range: number;

    // Constructor
    constructor(range: number, doors = 2) {
        super(doors); // スーパークラスのconstructorを実行
        this._range = range;
    }

    // Accessors
    get range() {
        return this._range;
    }
    set range(range) {
        this._range = range;
    }

    // Methods
    charge() {
        console.log(this.showDoor() + " is charging.")
    }
    // オーバーライド
    accelerate(): void {
        console.log('run electricCar accelerate.');
    }
}

```

 # 【7. ジェネリック】
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694199/6ab87af0-9fec-5017-019f-5ec78564d52f.png)
 ```ts:ジェネリック
function getArray<T>(items : T[]) : T[] {
    return new Array<T>().concat(items);
}
let numberArray: number[] = getArray<number>([5, 10, 15, 20]);
numberArray.push(25);       // OK
numberArray.push('test');   // ERROR

// 複数個
function identity<T, U> (value: T, message: U) : T {
    console.log(message);
    return value
}
let returnNumber = identity<number, string>(100, 'Hello!');
```

```ts:ジェネリック制約
type ValidTypes = string | number;

function identity<T extends ValidTypes, U> (value: T, message: U) {   // Return type is inferred
    let result: ValidTypes = '';
    let typeValue: string = typeof value;

    if (typeof value === 'number') {           // Is it a number?
        result = value + value;                // OK
    } else if (typeof value === 'string') {    // Is it a string?
        result = value + value;                // OK
    }

    console.log(`The message is ${message} and the function returns a ${typeValue} value of ${result}`);

    return result
}

let numberValue = identity<number, string>(100, 'Hello');
let stringValue = identity<string, string>('100', 'Hello');

console.log(numberValue);       // Returns 200
console.log(stringValue);       // Returns 100100
```

```ts:keyof
function getPets<T, K extends keyof T>(pet: T, key: K) {
  return pet[key];
}

let pets = { cats: 4, dogs: 3, parrots: 1, fish: 6 };

console.log(getPets(pets, "fish")); // Returns 6
console.log(getPets(pets, 1));      // ERROR
```

### ジェネリック インターフェイス
```ts:ジェネリック インターフェイス
interface Identity<T, U> {
    value: T;
    message: U;
}

let returnNumber: Identity<number, string> = {
    value: 25,
    message: 'Hello!'
}
```

```ts:ジェネリックインターフェースを関数型として宣言
interface ProcessIdentity<T, U> {
    (value: T, message: U): T;
}

function processIdentity<T, U> (value: T, message: U) : T {
    console.log(message);
    return value
}

let processor: ProcessIdentity<number, string> = processIdentity;
let returnNumber1 = processor(100, 'Hello!');   // OK
let returnString1 = processor('Hello!', 100);   // Type check error
```

```ts:ジェネリックインターフェースをクラス型として宣言
interface ProcessIdentity<T, U> {
    value: T;
    message: U;
    process(): T;
}

class processIdentity<X, Y> implements ProcessIdentity<X, Y> {
    value: X;
    message: Y;
    constructor(val: X, msg: Y) {
        this.value = val;
        this.message = msg;
    }
    process() : X {
        console.log(this.message);
        return this.value
    }
}

let processor = new processIdentity<number, string>(100, 'Hello');
processor.process();           // Displays 'Hello'
processor.value = '100';       // Type check error
```

### ジェネリッククラス
```ts
class processIdentity<T, U> {
    private _value: T;
    private _message: U;
    constructor(value: T, message: U) {
        this._value = value;
        this._message = message;
    }
    getIdentity() : T {
        console.log(this._message);
        return this._value
    }
}
let processor = new processIdentity<number, string>(100, 'Hello');
processor.getIdentity();      // Displays 'Hello'
```

## カスタムの型とクラスでジェネリックを実装
```ts
class Car {
    make: string = 'Generic Car';
    doors: number = 4;
}
class ElectricCar extends Car {
    make = 'Electric Car';
    doors = 4
}
class Truck extends Car {
    make = 'Truck';
    doors = 2
}
function accelerate<T extends Car> (car: T): T {
    console.log(`All ${car.doors} doors are closed.`);
    console.log(`The ${car.make} is now accelerating!`)
    return car
}

let myElectricCar = new ElectricCar;
accelerate<ElectricCar>(myElectricCar);
let myTruck = new Truck;
accelerate<Truck>(myTruck);

// 出力
// "All 4 doors are closed."
// "The Electric Car is now accelerating!"
// "All 2 doors are closed."
// "The Truck is now accelerating!"
```