仕事ですぐに使えるTypeScript その7

目的

学習の記録と学びになった部分をアウトプットするため。

期限

対象書籍の読了・内容理解が8月中に完了していること。

対象

今回の対象は「複合型」。

future-architect.github.io

著作権者:フューチャー株式会社(Future Corporation)様

複合型

三行サマリ

  • 他のプリミティブ型、もしくは複合型自身を内部で保持し、大きなデータを定義するデータ型をプリミティブ型という。プリミティブ型には配列やオブジェクトが該当する。

タプル型

  • Javaでは配列の中身は同じ型になる。しかし、TypeScriptでは1つ目の要素はstring, 二つ目の要素はnumberといった、複数の型を格納できるデータ型を「タプル型」という。
  • また、1つは必ず要素があり、2つ以上の要素が格納できるタプルを表現したい場合は、[string, ...string[]]と記載することで表現することができる。

配列からのデータの取り出し

  • 以前は配列やオブジェクトの要素を変数に取り出す際は一つずつしか取り出すことができなかったが、現在のJavaScriptでは分割代入で複数の要素をまとめて取り出すことができる。
const fruits = [
  "apple",
  "kabos",
  "banana"
];

const [red, green, yellow]  = fruits;
console.log(red);
// > apple
console.log(green);
// > kabos
console.log(yellow);
// > banana

const [, ...other] = fruits
// > [ 'kabos', 'banana' ]

配列の要素の存在チェック

  • 以前はindexOfを利用して判定していたが、現在はincludes() メソッドが入ったので、これを利用していった方が良い。

配列の加工

  • 最近のJavaScriptでは、排t列やオブジェクトを直接加工するのではなく、値が変更されたコピーを別で作成し、最後にリプレイスする方法が取られている。
  • スプレッド構文内でspliceを使用することで、配列のコピーも簡単に行うことができる。

配列のソート

  • 配列のソートにはsort()を使用する。
  • sortをする対象に数値が入っている場合は期待と異なる動作をするため、比較関数を引数に設定し、0より小さい数値(左辺を左側に)、0(等価)、0より大きい数値(左辺を右側に移動)を返すことで要素の並び替えのルールを設定することができる。
  • for文で繰り返す際は従来のfor文の実行速度が最も早く、それよりもfor...ofは速度面で劣ると言われている。

読み込み専用の配列

  • TypeScriptの const は変数の再代入をさせないガードにはなるが、変更不可にすることはできない。どういうことかというと、配列をconstで定義したとしても中の値は変更しうる可能性があるということ。
const fruits: string[] = [
    "apple",
    "kabos",
    "banana"
  ];
  
  fruits.push("orange");
  console.log(fruits);
// > [ 'apple', 'kabos', 'banana', 'orange' ]

constであっても上記のように配列に要素を追加することができるため、中の要素を変更したくない場合はreadonlyを定義することで変更不可にすることができる。

const fruits: readonly string[] = [
    "apple",
    "kabos",
    "banana"
  ];
  
  fruits.push("orange");
  console.log(fruits);
// > error TS2339: Property 'push' does not exist on type 'readonly string[]'.

readonlyを無理やり外したりせずに自然に使うためには、ソースコード全体でreadonlyを使用するように徹底する、もしくは全く使用しないようにする、二者択一になる。利用しているライブラリでもreadonlyを使用していないこともあるので、プロジェクト全体での意思統一が必要になる部分。

TypeScriptと配列

  • for..ofは従来のfor文よりもパフォーマンスは劣ると上記で記載をしたが、TypeScriptでfor..ofを使用している場合は、ES5への出力の場合は型情報を見て旧来のfor文がコンパイルされるので、速度上のペナルティがない状態で、最新の構文が使えるメリットがある。

JSON(JavaScript Object Notation)

  • JSONをパースするとオブジェクトと配列で階層構造になったデータが出来上がるが、通信用のライブラリではパース済みの状態でレスポンスが返ってくることもある。
  • JSONのキーは必ずダブルクォーテーションで囲む必要があり、配列やオブジェクトの末尾にカンマがあるとエラーになる。そのJSONJSON.parseを行うと、Syntax Errorの例外が発生する。

オブジェクトの加工

const smallAnimal = {
    name: "犬"
};

const attributes = {
    job: "パート",
    nearStation: "東京駅"
};

// 最近の傾向としてはObject.assignを使用するよりもスプレッド構文でコピーすることが多い。
// 旧
const copy1 = (<any>Object).assign({}, smallAnimal);
console.log(copy1);
// > { name: '犬' }

// 新
const copy2 = {...smallAnimal};
console.log(copy2);
// > { name: '犬' }

// オブジェクトのマージもObject.assignよりはスプレッド構文を使用する傾向
// 旧
const merge1 = (<any>Object).assign({}, smallAnimal, attributes);
console.log(merge1);
// > { name: '犬', job: 'パート', nearStation: '東京駅' }

// 新
const merge2 = {...smallAnimal, ...attributes};
console.log(merge2);
// > { name: '犬', job: 'パート', nearStation: '東京駅' }

参考:https://stackoverflow.com/questions/35959372/property-assign-does-not-exist-on-type-objectconstructor

Mapを使う

// 旧
var map1 = {
    "北海道": "ホッカイドウ",
    "青森": "アオモリ"
}

for (var key in map1) {
    if (map1.hasOwnProperty(key)) {
        console.log(key + " : " + map1[key]);
    }
}

// > 北海道 : ホッカイドウ
// > 青森 : アオモリ

/ 新
const map2 = new Map<string, string>([
    ["北海道", "ホッカイドウ"],
    ["青森", "アオモリ"]
])

for (const [key, value] of map2) {
    console.log(`${key} : ${value}`);
}

// > 北海道 : ホッカイドウ
// > 青森 : アオモリ

読み込み専用のオブジェクト

  • 配列はreadonlyをつけることで、中の要素も変更することができないよう定義することができたが、オブジェクトでも同様のことを行うことが可能。
  • しかし、配列で定義したようにreadonlyキーワードでは対応することができないので、型ユーティリティのReadOnly<>を使用する。
type User = {
    name: string,
    age: number
}

const u: Readonly<User> = {
    name: "田中",
    age: 10
}

console.log(u);
// > 

u.name = "佐藤";
// > error TS2540: Cannot assign to 'name' because it is a read-only property.