Open24

Typescript学び直し

bigbigcatatmospherebigbigcatatmosphere

パッケージマネージャ

NPM/NPX

  • package.json
    • アプリケーション情報
    • インストールしたモジュールのバージョンの依存関係
  • package-lock.json(yarn.json)
    • package.jsonとの利用モジュールの依存関係
    • モジュールの存在URL
  • node_modules
    • モジュール本体
    • ここは.gitignoreにいれる

※ npxはnode_modulesにインストールしたモジュールを実行する事ができる
内部的には npm execを実行しているだけ

bigbigcatatmospherebigbigcatatmosphere

宣言

var: 再宣言できる。使わない
  - 他にもグローバル変数として定義された場合、 windowオブジェクトのプロパティとして定義されるため、既存のプロパティを上書きする可能性がある。
  - 変数の巻き上げでエラー起きる(宣言前の変数を参照してもReference Errorが起きない)
- let,constも変数巻き上げは起きるが、undifinedで再代入が
let: 再宣言できない。再代入はできる
const: 再宣言できない。再代入できない。

極力constを使う。 for文とかしかletを使わないくらいのほうがいい。
読み手の負荷を下げることができる

ただし、constを使っても、
Array(配列), Object(オブジェクト)は参照型のデータなので、
変更ができる。(再代入はできない)

const myArr = [1, 2]
myArr[1] = 10
console.log(myArr) // [1, 10]

配列・オブジェクトを変更不可にする方法は別途用意されている

  • 配列・オブジェクト共通
    as constを使う。const assertionという。

  • 配列のみ。
    読み取り専用の型を使う。
    readonly T[], ReadOnlyArray<T>, 記法はどっちでもいい
    当たり前だけど、コンパイル時にエラーが出るだけで、
    エラーを無視してjsを実行すれば代入はできてしまう。

  • TS上で読み取り専用配列に代入したい場合
    型アサーションを使って、通常の配列に戻せば代入できる。(あまり使わないほうがいい)

  • readonlyas constの違い
    readonlyはオブジェクトのプロパティごとにつけられる。as constはすべてかつ再帰的。

型アサーション(type assertion)

value as T, <T> valueのどちらかで実行できる。
<>はJSXでも使ってるのでasを使ったほうが良さげ

TSにおけるType Assertionはキャストとは異なり、実行時に値の型を変換しているわけでないので、キャストと呼ばない。

型アサーションと型アノテーション(型注釈)という呼び分け。

bigbigcatatmospherebigbigcatatmosphere

制御フロー分析と型ガード

型アサーションを利用する場合、バグを産まないように、これらを利用する

  • ユニオン型の曖昧さがよくあるケース
 func doSomething(month :  string | number) {
    month.padStart(2, "0");  // 月を2桁にする関数
}

を定義した場合、このままだとnumber型を受けた際の処理が考慮できていない

  • 制御フロー分析=>「曖昧さが存在する場合、事前に型チェックをする」こと
 func doSomething(month :  string | number) {
   if typeof month === "string") {
    console.log(month.padStart(2, "0"));  // 月を2桁にする関数
   return;  // early return
   } 
   // elseを書かずにealry returnのほうがいい
  //  TSでは、このコードブロックまでたどりついた際にはmonthをnumber型と推論してくれるので
  //   number型のときだけ使えるメソッドを利用できる
 console.log(mont.toFixed());   
}

nulli type of value === "object" trueを返すので、 nullが混在する可能性がある制御フローでは、value != nullをand条件で指定する

  • 制御フロー分析
    ifやforループなどの制御フローを分析し、コードが実行されるタイミングでの型の可能性を判断すること

  • 型ガード

    • typeof演算子を使って、 確認する(これはjsの演算子なので"object"である、までしか判定できない)
    • instanceof演算子をつかって、クラスオブジェクトをより詳細まで判定する場合はこちら
     function getMonth(date: string | Date) {
           if (date instanceof Date) {
               console.log(date.getMonth() + 1);
           }
     }   
    
    • in演算子をつかう。特定のクラスのインスタンスであることを明示せず、オブジェクトが特定のプロパティをもつかを絞り込める。(インターフェース)
interface Wizard {
 castMagic(): void;
}
interface SwordMan {
 slashSword(): void;
}

function attack(player: Wizard | SwordMan) {
 if ("castMagic" in player) {   // これもクラスオブジェクトでしか使えない
   player.castMagic();
 } else {
   player.slashSword();
 }
}
  • アサーション関数を作る
    ガード関数との違いは、例外を投げる
function isDuck(animal: Animal): asserts animal is Duck {   // 戻りの型が asserts arg is T になるのがミソ
  if (walksLikeDuck(animal)) {
    if (quacksLikeDuck(animal)) {
      return;
    }
  }
 
  throw new Error("YOU ARE A FROG!!!");
}

asserts arg is Tの部分をType predicateという。
これは戻り値がboolean型の関数に対して利用できる。
trueを返す時のifのブロックの中では当該の変数をT型として解釈できるようになる

function isDuck(animal: Animal): animal is Duck {
  return animal instanceof Duck;  // instanceof ではクラスオブジェクトしか判定できない
}
if (isDuck(animal)) {
  animal.quacks();  // このブロックでは変数animalはDuck型であることがType predictで解釈される
  // ...
}

型ガードの変数代入。変数スコープを広くしたいときはこれ

function getMonth(date: string | Date) {
  const isDate = date instanceof Date;
  if (isDate) {
    console.log(date.getMonth() + 1);
  }
// 後続の処理でもisDateを利用できる、という書き方
}
bigbigcatatmospherebigbigcatatmosphere

変数スコープ

  • グローバルスコープ

    • ブラウザではwindowオブジェクトがグローバルオブジェクト
    • グローバル変数はグローバルオブジェクトのプロパティになる
      • Dateクラス, consoleオブヘクトなどの組み込みAPIはもれなくこれ。
      • グッローバルオブジェクトへのアクセスはwindowを省略できる
    • ローカルスコープ以外でvarを使用するとグローバル変数になる
    • varは関数スコープで定義されてしまうので、ブロックスコープで値を更新すると、影響を受ける
  • 関数スコープ

    • 関数内で参照できる
  • レキシカルスコープ

    • 関数の中から見て、参照できる関数の外の変数
  • ブロックスコープ

    • ブレース{}で囲まれた範囲
  • 意図しないグローバル変数の代入

    • jsでは、関数内にて宣言しない変数に代入した場合、グローバル変数を新規に作ってしまう
    • TSではこれは起きない。
bigbigcatatmospherebigbigcatatmosphere

変数宣言時の型推論について

TSではlet, constでの変数宣言時、型注釈をつけなくても 初期値を代入してあれば、
型を推論してくれる

let x = 1;
x = "hello";  // Type 'string' is not assignable to type 'numer'
bigbigcatatmospherebigbigcatatmosphere

プリミティブ型

  • イミュータブルな特性を持つ

  • 一方オブジェクト型(参照型)は、ミュータブル

  • プリミティブ型はプロパティを持たない。
    null, undifinedとか

  • ただし、stringや数値はプロパティを持ったオブジェクトとしても扱える。これがJSの特徴。
    -オートボクシングという。プリミティブ型をオブジェクト型に自動変換する

ボックス化について

JAVAなどとおんなじような思想で、JSにも、プリミティブ型に対するラッパーオブジェクトがある。
ラッパーオブジェクトをnewで作ると、ボックス化される。が、上述の通り、自動ボックス化されるので、
明示的にやる必要もない

プリミティブ型 ラッパーオブジェクト
boolean Boolean
number Number
string String
symbol Symbol
bigint BigInt
undefined なし
null なし
※ symbol型: 一意で不変の値

プリミティブ型とラッパーオブジェクト

  • 型注釈にラッパーオブジェクトは利用できるが、対応するプリミティブ型への代入できない。
  • ラッパーオブジェクト型は演算子が使えない。
  • ラッパーオブジェクトはインターフェースを満たせば他のオブジェクトも渡せる
const myboolean = {
  valueOf(): boolean {      // valueOfのインターフェースをもっていれば`Boolean`オブジェクトの代替となる
    return true;
  },
};
const bool: Boolean = myboolean;
  • ラッパーオブジェクトを型注釈に使う利点はない。

型強制(type coercion)

javascriptの話。暗黙の型変換 = 型強制

左辺・右辺どちらかに文字列型があり、+演算子が発生したときのみ、文字列としての加算を行う。
他の-, *, /などはnumber型に変換する

console.log(1 + "1"); // 11
console.log("1" + 1); //  11
console.log("1" - 1); // 0
console.log(1 - "1"); // 0
console.log("1" - "1"); // 0
bigbigcatatmospherebigbigcatatmosphere

リテラル型の細かなTIPS

数値リテラル

  • .で少数点が表現できる
0.1 === .1    // true
5.0 === 5.   // true 
  • 10進数以外の対応
0b1010 // 2進数
0o755 // 8進数
0xfff // 16進数
  • 数値の区切り(numeric separators)
100_000_000 // 1億

// 小数点の表現は使えない
 0_000_1   // これは使えない
_000_1      // これも使えない
100_._1     // これも使えない 
  • 数値リテラルのプロパティを参照する場合
  1. ()で囲む
  2. ..で参照する
(5).toString();
5..toString();
  • NaN、とInfinity
    NaNはnot-a-number。処理の結果数値にならない場合に返る。
const price = parseInt("百円");
console.log(price);

Number.isNaNメソッドで判定する

const price = parseInt("百円");
if (Number.isNaN(price)) {
  console.log("数値化できません");
}

NaNの比較は常にfalseが返る。

NaN === NaN; // false
Nan == Nan;  // fale

この性質を利用するとisNaNメソッドがかける

const isNaN: (value) => boolean{
  rerutn value !== value
}

Infinityが0で割ったときに返る。
TSでもエラーとしては検出しない。JSがInfinityを返す、という仕様のため、

  • 少数計算は誤差が出る
    循環小数を丸めたことによる丸め誤差である。
    桁数がわかっているときは、整数に桁上げした後、計算し、戻す、というやり方がいい。
    正確に計算したければdecimalをつかう
0.1 + 0.2 === 0.3; //=> false

BigInt関数

const x: bigint = 100n;
const x: bigint = BigInt("1000000000000000")
const x: bigint = BigInt("1_000")  // これは渡せない

numberbigintでは演算できないので、bigint関数で表現幅の広い方に合わせる

2n + 3; // -> エラー
2n + BigInt(3); //=> 5n
 

javascriptではNaN, Infinity, -Infinityという特殊な値がある
- const nan: number = 0/ 0
- inf: number = 1/0
- minusInf: number = -1/0

boolean型

  • 空文字列('')、数値の0undefinednullはif文においてはfalseとして扱われる
  • true, falseをつかう
  • 型の変換
    • Boolean(arg: Any)でboolean型に変更できる
    • !!で、その型のゼロ値であるか、を判断するboolean型に変換できる
const a: number = 1
console.log(a) // 1
console.log(!a) // false   -> number型のゼロ値である`0`ではないので falseが返る
console.log(!!a) // true  -> boolean型の`!a`でfalseのboolean型になって `!`でtrueになる。(つまり当該型のゼロ値判定後変数になる)
 
bigbigcatatmospherebigbigcatatmosphere

文字列型

  • "", '' はどちらでもいい
  • `` (バッククォート)はテンプレートリテラル(フォーマット文)、${式}で値を挿入できる
  • 利用順序のおすすめ
  1. 基本的に"を使用する
  2. 文字列の中に"が含まれる場合は'を使用する
  3. 文字列展開する必要があるときは`を使用する
bigbigcatatmospherebigbigcatatmosphere

null型とundefined型

  • nullはリテラルがある
const x: null = null;
  • undefinedはリテラルがない。グローバル定数的なもの。プリミティブな値。
const x: undefined = undefined;
  • 以下のようなときに発生する

    • 変数に値がセットされていない
    • 戻り値が無い関数、オブジェクトに存在しないプロパティにアクセス
    • 配列に存在しないインデックスでアクセス
  • 違い

    • undefinedは「値が代入されていないため、値がない」、nullは「代入すべき値が存在しないため、値がない」
    • nullは明示的に使用した場合しか発生しない
    • typeof演算子の扱いが異なる
  • 実用的な使い型はUnion型、Optional型で使う

  • undefine型 == 予期せぬ空値、 null型 == 明示的な空値

    • しかし、Tsではundefine型に統一すること後コーディングガイドラインには記載がある
typeof undefined;   // "undefined"
typeof null  // "object"
  • JSONの扱い
    • undefinedは未定義扱いなので、プロパティが存在しない、と扱う
    • nullは該当する値がないので、プロパティが存在する、と扱う
console.log(JSON.stringify({ foo: undefined }));
 //  =>{}
console.log(JSON.stringify({ foo: null }));
// => {"foo": null}
  • 値の比較
const a: null = null
console.log(a === undefined); // false
console.log(a == undefined); // true
console.log(a === null); // ture
console.log(a == null); // true
const b: undefined = undefined
console.log(b === undefined); //true
console.log(b == undefined); // true
bigbigcatatmospherebigbigcatatmosphere

シンボル型

シンボルはシンボル名が同じであっても、初期化した場所が違うとfalseになる。
同一の参照である必要がある。
JS本体の開発用に導入されているので、使わない。

const s1 = Symbol("foo");
const s2 = Symbol("foo");
console.log(s1 === s1);   // true
console.log(s1 === s2);  // false  -> tsではコンパイルエラー
bigbigcatatmospherebigbigcatatmosphere

リテラル型

特定の値のみを代入可能にする型。

let x: 1 = 1;
x = 1; // ok
x = 100; // '100' is not assignable to type '1'

つかえるのは以下のプリミティブ型

const isTrue: true = true;
const num: 123 = 123;
const str: "foo" = "foo";

使い所

ユニオン型と組み合わせ、マジックナンバー、ステートの表現に使う

let num: 1 | 2 | 3 = 1;
bigbigcatatmospherebigbigcatatmosphere

Any型

何でもOK(型チェックをパスさせる)

暗黙のany

型を省略してコンテキストから型が推論できない時、TypeScriptは暗黙的に型をany型として扱う。
バグの原因にはなりうる。
tsconfig.json のnoImplicitAny: trueが推奨(strict: trueでも有効になる)

bigbigcatatmospherebigbigcatatmosphere

オブジェクト

プリミティブ型以外は参照を返す。

  • リテラル
// 空っぽのオブジェクトを生成
const object = {};
 
// プロパティを指定しながらオブジェクトを生成
const person = { name: "Bob", age: 25 };
  • new演算子を使っても作れるが、上のほうがスマート
const person = new Object();
person.name = "Bob";
person.age = 25;
  • jsではオブジェクトのプロパティにメソッドを渡せる。
     - jsはオブジェクトのプロパティで値と、メソッドを分けていないので、値を設定しているプロパティ似関数を代入する、その逆もできてしまう。
const object = {
 // キーと値に分けて書いたメソッド定義
 printHello1: function () {
   console.log("Hello");
 },
 // 短い構文を用いたメソッド定義
 printHello2() {
   console.log("Hello");
 },
};
bigbigcatatmospherebigbigcatatmosphere

型エイリアス

typeキーワードを使う
型エイリアスは同じ型を再利用したいときに使うと便利です。型の定義が一箇所になるため、保守性が向上します。

また、型に名前を与えることで可読性が上がる場合があります。

type StringOrNumber = string | number;

// プリミティブ型
type Str = string;
// リテラル型
type OK = 200;
// 配列型
type Numbers = number[];
// オブジェクト型
type UserObject = { id: number; name: string };
// ユニオン型
type NumberOrNull = number | null;
// 関数型
type CallbackFunction = (value: string) => boolean;
bigbigcatatmospherebigbigcatatmosphere

型注釈

オブジェクト

;で定義する。

 let box: {
  width: number;
  height: number;
};
box = { width: 1080, height: 720 };
  • 型エイリアスでもできる
// 型エイリアス
type Box = { width: number; height: number };
let box: Box = { width: 1080, height: 720 };
//       ^^^型注釈

メソッドの型注釈

let calculator: {
  sum(x: number, y: number): number;
};
 calculator = {
  sum(x, y) {
    return x + y;
  },
};

// 関数型の書き方でもOK
let calculator: {
  sum: (x: number, y: number) => number;
};

⇢こっちの書き方のほうがいい。
なぜなら、引数の共変性を制限するtsconfig.jsonstrictFunctipnTypes: trueのオプションに乗っかれるから

引数の双変性

関数型の変数に対して、代入を行う際に、
その関数がとりうる引数の型の範囲を拡張・縮小をおこなえる性質

let func: (x: number | null) => any;

という型をもつfuncを定義すると。

funcには以下のような代入が可能

func = (n: number | null) => {}; // OK
func = (n: number | null | undefined) => {}; // OK  
func = (n: number) => {}; // OK

範囲を狭めることを引数の共変性といい
範囲を広めることを引数の反変性という
2つを合わせて引数の双変性という

共変性は型安全から外れるので、使いたくないので、
tsconfig.jsonstrictFunctipnTypes: trueをいれる。
ただし、この性質はメソッド型の記述ではチェックされない。
なのでオブジェクトにメソッドを定義する場合も関数型のほうがいい

interface Obj {
  // メソッド型
  method(n: number | null): any;
}
const obj: Obj = {
  method: (n: number) => {}, // チェックされない
};


interface Obj {
  // 関数型
  method: (n: number | null) => any;
}
const obj: Obj = {
  method: (n: number) => {}, // チェックが働く
};

※ 関数型 = :の後ろにメソッドのシグネチャを書く。型が関数であるというイメージ

bigbigcatatmospherebigbigcatatmosphere

オブジェクトの型推論

型を定義しなくても、初期リテラルから、推論をしてくれる

Record<Keys, Type>

連想配列を定義するときはこうする。
オブジェクト型との違い。 あくまで、キーは未定の値を取るが、key,valueには型がある

type StringNumber = Record<string, number>;
const value: StringNumber = { a: 1, b: 2, c: 3 };
//こう書くと、要素が増えていくものを想定している
value.d = 4
type StringNumber = Record<"a"| "b" | "c",  number>;
const value: StringNumber = { a: 1, b: 2, c: 3 };
//こう書くと、通常のオブジェクトの定義と一緒
value.d = 4 // これはコンパイルエラー
// つまり
type StingNumber = {
   "a": number;
   "b": number;
   "c": mumber;
}
と一緒

つまり、前者の方法での利用、 インデックス型を定義する1つの方法

他のインデックス型

type IndexKey: {
  [K: string]: number;
};

type IndexKey: {
  [key: string]: number;
};

tsconfig.jsonnoUncheckedIndexedAccessを有効にすると、
インデックス型のプロパティを代入する先の変数は当該型とundifined型とのUnion型となる

Mapped Types

インデックス型と異なり、キーは特定の値のみしか設定できない。
普通のオブジェクト型はこれに該当する。

  1. キーをちゃんと定義する(普通のオブジェクト型)
type MyMapp = {a: string; b:string}
  1. key in Unionを使う
type MyMapKey = "a" | "b";
type   MyMapp = {
       [key in MyMapKey]: string
}
  • 2の例がReadonly<T>
    • 内部的には各propertyにreadonly修飾子をつけている
type Readonly<T> = {
 readonly [P in keyof T]: T[P];
};
  • Mapped Typeでは追加のプロパティは定義できないので
  • インターセクション型を使って型定義をかえる
type KeyValues = {
  [K in string]: string;
};
type Name = {
  name: string; // 追加のプロパティ
};
type KeyValuesAndName = KeyValues & Name;
bigbigcatatmospherebigbigcatatmosphere

keyof型演算子

keyofはオブジェクト型からプロパティ名を型として返す型演算子

type Book = {
  title: string;
  price: number;
  rating: number;
};
type BookKey = keyof Book;
// 上は次と同じ意味になる
type BookKey = "title" | "price" | "rating";

インデックス型にkeyofを用いると、インデックスキーの型が返る。

type MapLike = { [K: string]: any };
type MapKeys = keyof MapLike; // string | number

↑ jsのMapオブジェクトはnumberをキーに指定しても、内部出来にはstring型で扱うので
myMap["0"]とmyMap[0]は等価なので、string | numberがインデックス型では返る

type MapLike = { [K: string]: any };
const myMap: MapLike = {
    1: 1,
    0: 2
}
console.log(myMap) //{ '0': 2, '1': 1 }

Mapped Typesにkeyofを用いる

type MapLike = { [K in "x" | "y" | "z"]: any };
type MapKeys = keyof MapLike;

プロパティを持たないオブジェクト型につかうとnever型が帰る

type What = keyof {};  // never

any型に使うと、オブジェクトのkeyをとれる、string | number | symbol型が返る

bigbigcatatmospherebigbigcatatmosphere

typeof型演算子

typeofは変数から型を抽出する型演算子。

const point = { x: 135, y: 35 };
type Point = typeof point; // type Poiint = { x: number; y: number}

jsのtypeof演算子とは別

typeof演算子

値の型を返すが、javascriptなので、ユーザ定義型は識別できない。

typeof true; //=> "boolean"
typeof 0; //=> "number"
typeof "Hello World"; //=> "string"
typeof undefined; //=> "undefined"
typeof null; //=> "object"    "null”がオブジェクトで返るのがミソ
typeof Symbol(); //=> "symbol"
typeof 1n; //=> "bigint"
typeof [1, 2, 3]; //=> "object"
typeof { a: 1, b: 2 }; //=> "object"
typeof (() => {}); //=> "function"

配列を判定する

typeof 演算子では配列はobject型なので、
専用にArray.isArray()メソッドがある

if (Array.isArray(n)) {
  // n is any[]
}

bigbigcatatmospherebigbigcatatmosphere

unknown型

unknown型は型安全なany型

unknown型の値は、具体的な型を持つ変数へ代入できない。
プロパティへのアクセス、メソッド呼び出しもできない
つまり、型を確定するまで何もできないようにしたいときに使う
unknown型の変数自体には代入できる

let newMyNumber: unknown = 0;
newMyNumber = 1;
console.log(newMyNumber)  // 1

let huga = newMyNumber // これはコンパイルエラー

unknown型を配列型に絞り込む

function isNumberArray(value: unknown): value is number[] {
  if (!Array.isArray(value)) {
    return false;
  }
  return value.every((e) => typeof e === "number");  // everyメソッドで要素の方まで確認する
}

uknown型をオブジェクト型へ絞り込む

  • typeofではobjectであることしか判別できない
  • instanceofはクラスじゃないので使えない
    =>そんなときの考え方
  1. パターン①
    これだとobject型であること検証していない。Email型であるかは確実ではない
type Email = {
  from: string;
  to: string;
  title: string;
  subject: string;
};
function isEmail(value: unknown): value is Email {
  if (typeof value !== "object" || value === null) {
    return false;
  }
  return true;
}
  1. パターン②
    なので、プロパティのチェックもしてみようとするが、
    valueにformというプロパティがあるか推論できないのでラーとなってしまう。
function isEmail(value: unknown): value is Email {
  if (typeof value !== "object" || value === null) {
    return false;
  }
  // 各プロパティのチェック
  if (typeof value.from !== "string") {   // valueにformというプロパティがあるか推論できないのでラエー
    return false;
  }
  return true;
}
  1. パターン③
    なので、型サーションを使いたい。
    (value as T) or (<T>value)
    しかしas Emailだと、 インターセクション型を使って型を拡張されていた場合がすり抜ける。
function isEmail(value: unknown): value is Email {
  if (typeof value !== "object" || value === null) {
    return false;
  }
  // 各プロパティのチェック
const email = value as Email;  
if (typeof email.from !== "string") { 
    return false;
  }
  return true;   // Email & {"huga": string} という型でもtrueを返してしまう。
}

ので、より安全にチェックするときには `Record<Key of T, known>を使う。


function isEmail(value: unknown): value is Email {
  if (typeof value !== "object" || value === null) {
    return false;
  }
  // 型アサーションでvalueをEmail型に近づける
  const email = value as Record<keyof Email, unknown>;
  // 各プロパティのチェック
  if (typeof email.from !== "string") {
    return false;
  }
  if (typeof email.to !== "string") {
    return false;
  }
  if (typeof email.title !== "string") {
    return false;
  }
  return typeof email.subject === "string";
}
bigbigcatatmospherebigbigcatatmosphere

uknown型で型アサーションを突破する

// 異なる型は指定できないが
const str = "a";
const num = str as number;

// unknwonを経由すれば行ける
const num = str  as uknown as number;

### useUnknownInCatchVariables
例外はunknwon型を明記することができるようになった
```ts
// case 1
try {
  throw new Error();
} catch (err) {         
  // err = any
}
 
// case 2
try {
  throw "This is an error!";
} catch (err: unknown) {         
  // err = unknown
}
 
// case 3
try {
  throw undefined;
} catch (err: unknown) {         
  // err = unknown
}

bigbigcatatmospherebigbigcatatmosphere

プロトタイプベース

jsはプロトタイプベース。
クラスベースはクラスという設計図からインスタンスを生成するという考え方だが、
プロトタイプベースは既存のインスタンス(オブジェクト)をもとにあたらしいインスタンス(オブジェクト)をつくる、と言う考え方。
なので、jsにもクラスはあるが、オブジェクトの一種であり、クラスを特別扱いしない。
オブジェクトに対して、Object.create()を実行することで、新しいオブジェクトを生成できる

const myObj = {name : "huga"}
const yourObjObject.create(myObj)

プロトタイプベースの継承

オブジェクトを作った後に足したい振る舞いを追加する。という形。extendsなどのキーワードはない

const counter = {
  count: 0,
  countUp() {
    this.count++;
  },
};
const resettableCounter = Object.create(counter);
resettableCounter.reset = function () {
  this.count = 0;
};

が、普通にクラスが使えるようになっている
classで定義し、extendsで継承する

class ResettableCounter extends Counter {
  reset() {
    this.count = 0;
  }
}
bigbigcatatmospherebigbigcatatmosphere

object, Object, {}の違い

一般的な型注釈と異なり、プリパティの型を指定せず、
ざっくり「オブジェクトであること」を型注釈することがある。

object型

object型では、オブジェクト型の値だけが代入できる。つまり、プリミティブ型は代入できない

Object型

Object型はインターフェース。vaueOfなどのプロパティを持つ値なら何でも代入できる。
Object型にはnullやundefinedを除くあらゆるプリミティブ型も代入できる。
(文字列型や数値型などのプリミティブ型もオートボックシングにより、
オブジェクトのプロパティをもつため。)
※こちらは利用非推奨。プリミティブ型が大有できてしまうため。object型の利用を推奨

{}型

プロパティを持たないオブジェクトを表す。
プロパティを持つ値なら、null, undefinedを除く、全て代入できる

bigbigcatatmospherebigbigcatatmosphere

オブジェクトの分割代入

複数のプロパティを取り出す

const item = { price: 100 };
const { price } = item;
// 上は const price = item.price; と同等の処理

const obj = { a: 1, b: 2 };
const { a, b } = obj;

代入する変数名の指定

const color = { r: 0, g: 122, b: 204, a: 1 };
const { r: red, g: green, b: blue, a: alpha } = color;
console.log(green);

入れ子の分割代入

入れ子で指定すれば値を取り出せる

const continent = {
  name: "北アメリカ",
  us: {
    name: "アメリカ合衆国",
    capitalCity: "ワシントンD.C.",
  },
};

const {
  us: { name, capitalCity },
} = continent;

入れ子構造の分割代入と変数名の指定

const continent = {
  name: "北アメリカ",
  us: {
    name: "アメリカ合衆国",
    capitalCity: "ワシントンD.C.",
  },
};
 
const {
  name: continentName,
  us: { name: countryName },
} = continent;
bigbigcatatmospherebigbigcatatmosphere

shorthand property

オブジェクトのキーと変数名が同一の場合に限り、以下のようにもかける
(多分つかわない)

type Wild = {
  name: string;
  no: number;
  genre: string;
  height: number;
  weight: number;
};
 
const name = "pikachu";
const no = 25;
const genre = "mouse pokémon";
const height = 0.4;
const weight = 6.0;
 
const pikachu: Wild = {
  name,
  no,
  genre,
  height,
  weight,
};