はじめに
TypeScript、難しいですよね(少なくとも私は得意ではありません)? 特に抽象度が高くなるとすぐに脳内メモリが足りなくなるので、なんとなくの実装で乗り切ってきたのですが...。そうこうも言ってられなくなったので(遅い)、曖昧なところを潰してみることにしました。
▼前回の記事はこちら
https://qiita.com/sazumy/private/7c4f35500aa51210a4d4
使用テキスト
使用したのはこちらのテキスト。コードもこちらの書籍から引用しています。全てのコードには該当ページ数も書いておきます。
ユニオン型
まずはユニオン型です。ユニオン型は「TまたはU」という状態を表す方で、T | Uと書きます。具体例はこんな感じです。Animal
またはHuman
の型を持つUser
という方は、以下のような事例に使うことができます。
type Animal = {
species: string;
}
type Human = {
name: string;
}
type User = Animal | Human;
// User型はnameまたはspeciesのプロパティを持つので、これはコンパイルが通る
const tama: User = {
species: "cats"
}
// これもコンパイルが通る
const firstUser: User = {
name: "taro"
}
// titleというプロパティはUser型にないので、これはコンパイルエラー
const book: User = {
title: "The Tale of Peter Rabbit"
}
// これもコンパイルが通る???
const pochi: User = {
species: "dogs",
name: "pochi"
}
プロを目指す人のためのTypeScript入門:p.248のコードを改変して使用
解せないのは、species
とname
の両方のプロパティを持つ、以下のオプジェクトもコンパイルが通ってしまうこと。
const pochi: User = {
species: "dogs",
name: "pochi"
}
TかつUという型を作るのは、この記事の下方で挙げているインターセクション型の役割じゃなかったっけ?
...結局この点については解消しなかったのですが、暫定的に上記の時点では、pochi
はAnimal
であるかHuman
であるか確定していないからという結論になりました。誰か正しい答え知っていたら教えてください...
なお、ユニオン型は下記のように書くことも可能です。
type User =
| Animal
| Human
実務のコードで時々見ますが、そういう意味だったのかと改めて確認
インターセクション型
インターセクション型は「TかつU」を表す型で、T & Uと書きます。そして、オブジェクト型を拡張する新しい型を作ります。
具体例は以下のようになります。
type Animal = {
species: string
}
type Human = {
name: string
}
type User = Animal & Human;
const pochi: User = {
species: "dogs",
name: "taro"
}
// こんな風にオブジェクト型ベタ書きでも型を拡張できる
type AnimalWithAge = Animal & { age: number };
const tama: AnimalWithAge = {
species: "dogs",
age: 5
}
プロを目指す人のためのTypeScript入門:p.253のコードを改変して使用
こちらはとても簡単ですね
個人的には Animal & { age: number }
みたいに、オブジェクトの型 & オブジェクトの型のベタ書きで拡張できるのがよかったです。色々柔軟に考えられそう。
ユニオン型とインターセクション型の練習
頭の体操的な記載も一つ載っていたので試してみることにしました。
このような、Human
型のオブジェクトを受け取ったらname
を、Animal
型のオブジェクトを受け取ったらspecies
を返すメソッドを作ってみました。
const tama: Animal = {
species: "cats"
}
const firstUser: Human = {
name: "taro"
}
const getName = (human: Human) => {
return human.name;
}
const getSpecies = (animal: Animal) => {
return animal.species;
}
// randomFuncは、const randomFunc: ((human: Human) => string) | ((animal: Animal) => string) というユニオン型
const randomFunc = Math.random() < 0.5 ? getName : getSpecies
// ここでコンパイルエラーになる
randomFunc(tama)
randomFunc(firstUser)
プロを目指す人のためのTypeScript入門:p.255のコードに追記して使用
randomFunc
は((human: Human) => string) | ((animal: Animal) => string)
というユニオン型ですが、呼び出す際にHuman
型かAnimal
型のどちらかのオブジェクトを入れて呼び出すとエラーになります。
そこで、tama
とfirstUser
にHuman & Animal
型である以下の型を適用。
// ここをHuman & Animal型に
const tama: Human & Animal = {
name: "tama",
species: "cats"
}
// ここもHuman & Animal型に
const firstUser: Human & Animal = {
name: "taro",
species: "human"
}
const getName = (human: Human) => {
return human.name;
}
const getSpecies = (animal: Animal) => {
return animal.species;
}
const randomFunc = Math.random() < 0.5 ? getName : getSpecies
// コンパイルエラーが出ない
randomFunc(tama)
randomFunc(firstUser)
プロを目指す人のためのTypeScript入門:p.256のコードに追記して使用
無事、エラーなく呼び出せました
感想
TypeScript...今でも苦手意識強いのですが、一つ一つ読み解いていくことで何とか実務でも落ち着いて取り組めそうです。
アドベントカレンダーという機会があってやってみてよかったです。