🐓

TypeScript 学習記録 #4(オブジェクトと配列の型定義)

commits5 min read

TypeScript 学習記録 #3 の続編。
今回のメインテーマ、「オブジェクトと配列の型定義について」

今回主に参考にした教材:

見返す用リスト(主に自分用)

TypeScript基礎学習を完走したので、いつでも見返すことの出来るようにリスト化しておく(5/17)

オブジェクトの型定義

以下のことについて学んだ。

  • オブジェクトの型定義
    • オブジェクトの構造を型として定義する
    • オプショナル?のついたプロパティ(あってもなくてもOK)
    • readonlyのついたプロパティ(上書き不可)
    • インデックスシグネチャ
    • 型エイリアス(型の再利用)
    • 合併型(Union Type)と交差型(Intersection Type)

合併型(Union Type)と交差型(Intersection Type)

「A」という型と「B」という型の2つがあるとき、

  • 合併型(Union Type):「A」または「B」どちらかの型を持つ(A | B
    • プリミティブ型でよく使うらしい(例:type id = string | number
  • 交差型(Intersection Type):「A」と「B」両方の型を持つ(A & B
    • 「A」と「B」に含まれる属性を全て含めないとエラーになる

合併型(Union Type)は、「A」と「B」どちらかの型を持つと書いたが、実際には両方の型を同時に持つことも出来てしまう交差型に近いことが出来てしまう)ため、オブジェクト同士の合併型はあまり使われないらしい。上述の通り、よく使われるのはプリミティブ型とのこと。

交差型(Intersection Type)は、「A」と「B」両方の型に共通する型を持つのではなく、シンプルに「Aの型」+「Bの型」というハイブリッド版の型になる点に注意。
集合の知識がある人ほど誤解しやすそう。「且つ」よりも「足す」が近い気がする。

以下は合併型と交差型の例(詳しくは動画参照)

// RPGの職業のお話
type Knight = {
 hp: number;
 sp: number;
 weapon: string;
 swordSkill: string;
};

type Wizard = {
 hp: number;
 mp: number;
 weapon: string;
 magicSkill: string;
};

// 合併型(Union Type)
// 初期職種:冒険者(`Knight`または`Wizard`の型を持つ(=騎士または魔法使いになれる))
type Adventurer = Knight | Wizard;

// 交差型(Intersection Type)
// `Knight`と`Wizard`両方の型を持つ(共通部分のみの型を持つのではないことに注意)
type Paladin = Knight & Wizard;

// Knight寄りの冒険者
const player1: Adventurer = {
 hp: 100,
 sp: 30,
 weapon: "木の剣",
 swordSkill: "みだれ斬り",
};

// Wizard寄りの冒険者
const player2: Adventurer = {
 hp: 100,
 mp: 30,
 weapon: "ひのきの杖",
 magicSkill: "メラミ",
};

// パラディン(`Knight`と`Wizard`両方の型を兼ね備える。1つでも属性が欠けるとエラーとなる)
const paladin: Paladin = {
 hp: 300,
 sp: 200,
 mp: 200,
 weapon: "天空の剣",
 swordSkill: "ギガスラッシュ",
 magicSkill: "メラゾーマ",
}

// 合併型(Union Type)の注意点
// 合併型は`Knight`と`Wizard`両方の型を持ててしまうので、以下のように、一方の型の最低限の要素を満たしていれば、もう一方の型の要素を含めることが出来てしまう点に注意する。
const player3: Adventurer = {
 hp: 100,
 sp: 30,
 weapon: "木の剣",
 swordSkill: "みだれ斬り",
 magicSkill: "ベギラマ", // このように`Wizard`の型も混ぜることが出来てしまう
}


合併型(Union Type)のとてもわかりやすい例を見つけたので紹介(参考

// 自動販売機に入れることが出来るお金の種類を型として定義する

// 硬貨類(硬貨は`10円`か`50円`か`100円`か`500円`のうちのいずれかしか入らない)
type Coin = 10 | 50 | 100 | 500;

// 紙幣類(紙幣は`1000円札`しか入らない)
type Bill = 1000;

// 上記の2つを合併型(Union Type)で結合
type Money = Coin | Bill;

// 引数moneyは、Money型で定義された属性のうちのいずれかを満たすもののみ入れることができる
const insertMoney = (money: Money) => { ... };
insertMoney(10000); // 10000円札は型定義によって入れることができない

配列の型定義

以下のことについて学んだ。

  • 配列の型定義
    • 基本的な配列の型定義
    • タプル(厳格な配列)の型定義
    • イミュータブルな配列の型定義

配列の型定義(2種類)

配列の型定義の方法は2種類ある。どちらの方がいいんだろうか。

// 配列の型定義(2種類ある)
const odd: number[] = [1, 3, 5]; // T[]
const even: Array<number> = [2, 4, 6]; // Array<T>

// 合併型(Union Type)も使える(あまり使う場面はないらしい)
const ids: (string | number)[] = ["aiko", 45];
const ids: Array<string | number> = ["aiko", 45];

個人的には「配列」であることが明確に書いてあるArray<T>の方が好きだが、よく参考にしているソースコードを見てみた感じ、T[]の方がよく使われていた。
これは個人的推測だが、Array<T>と書くと型エイリアスの大文字から始まる型と混同して少し紛らわしい気がしないでもないので、そのこともあってT[]の方が好まれているのだろうか。
ひとまず先人のコードに倣って、T[]を使おうと思う。

ここまでの感想

オブジェクトと配列の型定義の方法を理解した。
ここまでで「基本的な型」、「関数の型定義」、「オブジェクトの型定義」、「配列の型定義」の方法を理解したので、恐らくもう簡単なものであればTypeScriptを用いて何かしら作ることが出来るレベルだと思う。

トラハックさんの動画は残り、「ジェネリック型」、「クラスの型」、「interfaceとtype alias」、「非同期処理の型定義」の4本なので、ひとまずはその動画を見終えることを目標としつつ、並行してReactで使う型(PropsやReact hooksなど)の型定義方法についても別途調べ始めようかと思う。

オブジェクトの型エイリアスなどを学んで凄く実感したが、TypeScriptで確りと型定義をした時の「予測変換」の体験が良すぎる。このおかげでシンプルにタイピング量が減るし、またタイポする可能性も減らせるというので、この体験はとても素晴らしいなと感じた。
TypeScriptをやっていると、コード自体がドキュメントになるという気分がだんだん分かってきた。

GitHubで編集を提案

Discussion

ログインするとコメントできます