TypeScriptの仕様・パターン覚え書き

## 基本の型指定

### 基本
Number: 数値
String: 文字
Boolean: true / false
Any: 全ての型
Void: 返り値がない
undefined: undefined
null: null

### 配列

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

### Tuple型

```
// 0番目にnumber、1番目にstringのみ入れることができる
let tuple: [string, number] = ["myname", 3];
```

### Enum形

デフォルト値なし

```
// 型の定義
enum Family {Rick, Mike,Json};

// 使用するとインデックス番号が入る
let Rick:number = Family.Rick; // 0
let Mike:number = Family.Rick; // 1
let Json:number = Family.Rick; // 2

```

デフォルト値あり

```
// 型の定義
enum Ages {Rick = 21 , Mike = 22 , Json = 23};
// 使用するとデフォルト値が入る
let Rick:number = Family.Rick; // 21
let Mike:number = Family.Rick; // 22
let Json:number = Family.Rick; // 23
```

文字列はエラー

```
// エラー
enum Hello {Rick = "Rick dayo" , Mike = "Mike dayo" , Json = 'Json dayo'};
```

インデックスを指定してキーを取り出す

```
// 型の定義
enum Ages {Rick = 21 , Mike = 22 , Json = 23};
let Rick:string = Family[21]; // Rick
let Mike:string = Family[22]; // Mike
let Json:string = Family[23]; // Json
```

### Never型
・returnが絶対に走らない場合、戻り値がない場合never型になる
・値は入れられない

```
// errorを返す関数
const error = ():never => {
    throw new Error("エラー");
}
```

### 関数

functionキーワードによる関数定義

```
function bmi(height: number, weight: number):number {
	return weight / (height * height);
}
```

無名関数による関数定義

```
let bmi = function (height:number, weight:number):number {
	return weight / (height * height);
}
```

### Restパラメータを設定する

```
const sum = (...values: number[]):number => {
	console.log(values); // [0,1,2,3,4]
	return 100;
}
sum(1,2,3,4);

// reduceを使う
const reducer = (accumulator: number, currentValue: number):number => {
	console.log({accumulator, currentValue});
	return accumulator + currentValue;
}
[1,2,3,4].reduce(reducer); 
// {accumulator:1, currentValue:2} 
// {accumulator:3, currentValue:3} 
// {accumulator:6, currentValue:4}
```



### voidとneverの違い

Void型
・戻り値undefinedを含む
・returnを省略したり、戻り値のないreturnをするとundefinedが返る

Never型
・neverはそもそもreturnをしない。
・例外を投げる場合や無限ループをする場合など、戻り値が得られないときに使う
・undefinedも受け付けないので、戻り値の型にneverを指定した場合はreturnは書けない。

```
onst void1 = () => {}
const void2 = () => { return }
const never = () => { while(true){} }

let voidFunc1:never = void1() // エラー
let voidFunc2:never = void２() // エラー
let neverFunc:never = never() // OK　
```

### Object型
・プリミティブ型ではないことを表現する型

```
// プリミティブ型を引数にとらないようにする関数
function viewObject(o: object): void {
  console.log(`myData: ${o}`);
};
```

### インターフェース
オブジェクトが特定の構造が合っているかどうかチェックする。

```
interface Props {
  name: string;
}

function getMyname(props:Props): string {
  return `I'm ${props.name}`;
}

```

### オーバーロード
シグネチャ = 関数の実態は書かずに、引数戻り値などの型を設定したもの

```
function double(value:number):number {
	return value * 2;
}

function double(value:string):string {
	return value + value;
}
// 被っているのでエラーとなる。 この場合、シグネチャを使う

↓

// シグネチャの宣言
function double(value:number):number;
function double(value:string):string;

function double(value: any): any { // 型制約はシグネチャで定義して、関数の実態では全ての型を受け取れるようにする
	if(typeof value === "number") {
		return value * 2;		
	} else {
		return value + value;
	}
}

```

### クラス
```
class Person {
	name: string;
	age: number;

	constructor(name: string, age: number) { // constructorもメソッド(関数)なので引数が必要
		this.name = name;
		this.age = age;
	}
	
	profile(): string {
		return `name; ${this.name}, age: ${this.age}`
	}
}
let taro = new Person("taro", 30);
console.log(taro);
```

### アクセス修飾子

```
class Person {
	public name: string; // インスタンスからアクセスできる
	private age: number; // インスタンスからはアクセスできない
	protected nationality: string; // 子クラス(サブクラス)からもアクセスができる

	constructor(name: string, age: number, nationality: string) { // constructorもメソッド(関数)なので引数が必要
		this.name = name;
		this.age = age;
		this.nationality = nationality;
	}
	
	profile(): string {
		return `name; ${this.name}, age: ${this.age}`;
	}
}

class Android extends Person { // Personクラスを継承
	constructor(name: string, age: number, nationality: string) {
		super(name, age, nationality); // 親クラスのconstructorを使う
	}
	profile(): string {
		// エラー: ageはprivateなので、小クラスで使えない
		return `name; ${this.name}, age: ${this.age}, nationality: ${this.nationality}`;
	}
}

let taro = new Person("taro", 30, "Japan");

console.log(taro.name); // アクセスできる
console.log(taro.age); // アクセスできない
```

### constructor
アクセス修飾子にconstructorに記載すると、初期化処理がされる

```
class Person {
	constructor(public name: string, protected age: number) {}
}

const me = new Person("Ogura", 100);
console.log(me); // {name; "Ogura", age: 100}
```

### getterとsetter
```
class MyNumberCard {
	private _owner: string; // メンバ変数がget ownerと名前が被るので、アンダーバーをつける
	private _secretNumber: number;

	constructor(owwner: string, secretNumber: number) {
		this._owner = owner;
		this._secretNumber = secretNumber;
	}

	get owner() {
		return this._owner;
	}

	set secretNumber(secretNumber: number) {
		thIs._secretNumber = secretNumber;
	}

	debugPrint() { // 変更を確認するメソッド
		return `secretNumber: ${this._secretNumber}`;
	}
}

```

### readonly修飾子
```
class VisaCard {
	readonly owner: string;
	constructor(owner: string) {
		this.owner = owner;
	}
}

let myVisaCard = new VisaCard("Rick");
console.log(myVisaCard.owner); // Rick
myVisaCard.owner = "Eddie"; // readonlyなのでエラー
```

省略した書き方もできる

```
class VisaCard {
	constructor(public readonly owner: string) {}
}

```

readonlyの場合、アクセスできることが前提なので、さらにpublicを省略できる

```

class VisaCard {
	constructor(readonly owner: string) {}
}
```

### namespaceによる名前空間
複数のPersonクラスを定義するために、namespaceに閉じ込める

```
namespace Japanese {
	export class Person { // exportがないとJapanese.Personとしてもアクセスできない
		constructor(public name:string){}
	}
}

namespace English {
	export class Person {
		constructor(
			public firstName:string, 
			public middleName:string, 
			public lastName:string
		){}
	}
}
const me = new Japanese.Person("Gura");
console.log(me.name); // Gura
const michel = new English.Person("Michel", "Mike", "Jhonson");
```



ネストする場合

```
namespace Japanese {
	export namespace Tokyo {
		export class Person { 
			constructor(public name:string){}
		}
	}
	export namespace Osaka {
		export class Person { 
			constructor(public name:string){}
		}
	}
}
const me = new Japanese.Tokyo.Person("Gura");
const meOsaka = new Japanese.Osaka.Person("Guraやで");
```

### 継承
基本

```
class Animal {
	run(): string {
		return "I can run";
	}
}
class Lion extends Animal {
	run(): string {
		return "I can run 80km.";
	}
}
let animal = new Animal();
console.log(animal.run()); // I can run
let lion = new Lion();
console.log(lion.run()); // I can run 80km.

```

Constructorを追加

```
class Animal {
	constructor(public name: string) {}
	run(): string {
		return "I can run";
	}
}
class Lion extends Animal {
public speed: number;
	constructor(name: string, speed: number) {
		super(name);
		this.speed = speed;
	}
	run(): string {
		return `${super.run()} ${this.speed}km.`; // 親クラスのメソッドrunを呼び出す
	}
}

console.log(new Animal("Mickey").run()); // I can run
console.log(new Lion("Simba", 80).run()); // I can run 80km.
```

### 抽象クラスと抽象メソッド
```
abstract class Animal {
	abstract cry(): string;
}

class Lion extends Animal {
	cry() {
		return "roar";
	}
}
```

### インターフェースで二つの親クラスを継承する
ダメなパターン

```
class Sword {}
class Momo {}
class Momotaro extends Sword, Momo {} // エラー:親クラスを２つ継承できない
```

interfaceとimplementsを使う

```
interface Momo {
	bougyo(): void;
}
interface Sword {
	kougeki(): void;
}
class Momotaro implements Momo, Sword {
	bougyo(): void {
		console.log("mamoruyo");
	}
	kougeki(): void {
		console.log("atetayo");
	}
}
const momotaro = new Momotaro();
momotaro.bougyo();
momotaro.kougeki();
```

## 高度な型

### ジェネリクス

関数

```
const echo = <T>(arg: T): T => {
	return arg;
}
console.log(echo<number>(100));
```

クラス

```
class Mirror<T> {
	constructor(public value : T) {}
	echo(): T {
		return this.value;
	}
}
console.log(new Mirror<number>(123).echo());
```

### 型アサーション (型を示す)
```
let length = name.length as number;
```

別パターン1

```
let length = (name as string).length; // 変数lengthの型はnumber
```

別パターン2 (非推奨)

```
let length = (<string>name).length; // 変数lengthの型はnumber
```

### constアサーション (型の書き替えを行えない)

変数

```
let nickname = "Gura" as const; // nickname: "Gura"型
nickname = "Ogura"; // エラー: "Ogura型に変更できない
```

オブジェクト

```
let  profile = {
	name: "Atshushi", // readonly name
	height: 178  // readonly height
} as const;
profile.name = "Ash"; // エラー: as constのおかげでreadonly型になっている
```

### インデックスシグネチャ

ダメな例
nameプロパティは追加できても、ageを追加したらまたエラーが出る

```
let profile: {name?: string} = {};
profile.name = "Gura";
profile.age = 43; // Error
```

インデックスシグネチャを使う

```
let profile: { [index: string]:string | number } = {};
profile.name = "Gura";
profile.age = 43;
```

## Utillity Types / Conditional Types

### 全てをオプショナル、またはRequiredにするUtillity Types
```
type Profile = {
	name: string;
	age: number;
}

// 型引数にProfileを渡す
type PartialType = Partial<Profile>; // 全てオプショナル に
type RequiredType = Required<Profile>; // 全て必須 に
```

### keyof と in、Mapped Types
keyof

```
type Profile = {
	name: string;
	age: number;
}
type PropertyTypes = keyof Profile; // PropartyTypes = "name" | "age";
```


keyof と inを使い、Partialに似た独自のUtillity Typesを作る(Mapped Types)

```
type Optional<T> = {
	[P in keyof T]?: T[P] | null; // inを使い1個ずつ取り出す、Pは"name"または"age"になる
} 
type Optional Profile = Optional<Profile>;
```

### Readonly

```
type Profile = {
	name: string;
	age: number;
}

type PersonalDataType = Readonly<Profile>;

const friend:PersonalDataType = {
	name: "Shigeru",
	age: 40,
}

friend.age++; // エラー: Readonlyなのでインクリメントできない
```

### Record

```
type Prefectures = "Tokyo" | "Kyoto" | "Osaka" | "Nagoya";

type NinjaInfo = {
	kanji_name: string;
	number_of_people: number;
}

const ninjaInJapan: Record<Prefectures, NinjaInfo> = {
	Tokyo: { kanji_name: "東京", number_of_people: 1960 },
	Kyoto: { kanji_name: "京都", number_of_people: 249 },
	Osaka: { kanji_name: "大阪", number_of_people: 2 },
	Nagoya: { kanji_name: "名古屋", number_of_people: true }, // エラーがちゃんと出る
}
```

### 不要な型を排除する(Exclude) / 特定の型を取り出す(Extract)
不要な型を排除する: Exclude<全体の型, 削除したい型>

```
type DebugType = () => void;
type SomeTypes = string | number | DebugType;

// Exclude
type FunctionType = Exclude<SomeTypes, string | number>; // type FunctionType = () => void;
type NunFunctionType = Exclude<SomeTypes, DebugTypes>; // NunFunctionType = string | number;
type TypeExcludingFunction = Exclude<SomeTypes, Function>; // TypeExcludingFunction = string | number;
```

特定の型を取り出す: Extract<全体の型, 取り出したい型>

```
type DebugType = () => void;
type SomeTypes = string | number | DebugType;

type FunctionTypeByExtract = Extract<SomeTypes, DebugType>; // FunctionTypeByExtract = () => void;
type NonFunctionTypeByExtract = Extract<SomeTypes, string | number>; // NonFunctionTypeByExtract = string | number;
type FunctionTypeExtractingFunction = Extract<SomeTypes, Function>; // FunctionTypeExtractingFunction = () => void
```

### Nullやundefinedを排除する(NonNullable)
NonNullable<nullとundefinedを取り出したい型>

```
type NullableTypes = string | number | null | undefined;
type NonNullableTypes = NonNullable<NullableTypes>; // type NonNullableTypes = string | number;
```

### 必要なものだけ取り出して新たな型を作る(Pick) 
```
type DetailedProfile = {
	name: string;
	height: number;
	weight: number;
}

// Pick<参照する型, 取り出したい型 | 取り出したい型>
type SimpleProfile = Pick<DetailedProfile, "name" | "weight">; // type SimpleProfile = {name: string, weight: number;}
```

### 特定の型を除外して新たな型を作る(Omit)
```
type DetailedProfile = {
	name: string;
	height: number;
	weight: number;
}

type SmallProfile = Omit<DetailedProfile, "height">; // type SmallProfile = {name: string, weight: number;}

```

### 関数の戻り値の型を取得する(ReturnType)
```
function add(a: number, b: number) {
	return a + b;
}

type ReturnTypeFromAdd = ReturnType<typeof add>; // type ReturnTypeFromAdd = number;
```

### 関数が受け取る引数と同じ構造の型を作る(Parameters)
```
const debugProfile = (name: string, age: number) => {
	console.log({ name, age });
}

debugProfile("Ham", 43);

type Profile = Parameters<typeof debugProfile>; // type Profile = [string, number];

const profile: Profile = ["Green", 21];
const profile: Profile = [32, "Mynames"]; //  tuple型なのでエラー
```

### constructorが受け取る引数と同じ構造の型を作る(ConstructorParameters)
```
class Person {
	name: string;
	age: number;

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

let taro = new Person("Taro", 30);

type PersonType = typeof Person; // type PersonType = typeof Person;

type Profile = ConstructorParameters<PersonType>;  // type Profile = [string, number];

const profile: Profile = ["Ham", 43];
const ham = new Person(...profile);
```