値のとりうる形をしぼる

INPUT · スライド

目印で見分けるユニオン

01 / 05

`typeof` では見分けられない

ここまでの絞りこみは typeof が主役だった。文字列なら "string"、数値なら "number" が返ってくるから、それで分けられたね。

でもオブジェクトどうしのユニオンには効かない。どちらも "object" が返ってくるからだよ。これでは「いぬの形」と「とりの形」を分けられない。

const inu = { namae: "ぽち" };const tori = { tobu: true };console.log(typeof inu);console.log(typeof tori);

実行結果

object
object

02 / 05

共通の目印を1つ持たせる

そこで、どちらの型にも同じ名前のプロパティを1つ足す。中身はリテラル型にして、メンバーごとにちがう値を入れておく。これが目印だよ。

目印は両方の型にあるので、絞りこむ前でもそのまま読める。そして if で目印を見ると、その中では片方の型だと分かってもらえる。だから Inu だけが持つ namae も安全に使えるんだ。

type Inu = {  shurui: "inu";  namae: string;};type Tori = {  shurui: "tori";  tobu: boolean;};function dasu(a: Inu | Tori): void {  if (a.shurui === "inu") {    console.log(a.namae);  } else {    console.log(a.tobu);  }}dasu({ shurui: "inu", namae: "ぽち" });dasu({ shurui: "tori", tobu: true });

実行結果

ぽち
true

03 / 05

目印が無いと片方のものが使えない

目印を持たせなかったらどうなるか、見ておこう。| でつないだだけの型では、両方にあるプロパティしか使えない

Tori には namae が無いので、a.namae はその場で止められる。エラーは2行に分かれて出て、下の行が「どのメンバーに無いのか」を名指しで教えてくれるよ。

type Inu = {  namae: string;};type Tori = {  tobu: boolean;};function dasu(a: Inu | Tori): void {  console.log(a.namae);}

実行結果

型のエラー: プロパティ 'namae' は型 'Inu | Tori' に存在しません。
  プロパティ 'namae' は型 'Tori' に存在しません。

04 / 05

目印はリテラル型でないと効かない

もう1つの落としあなが、目印を string で書いてしまうこと。shurui: string だと、どちらのメンバーも「なんらかの文字列」を持っているだけになる。

すると if (a.shurui === "inu") を書いても何も絞りこめない。中でも Inu | Tori のままなので、さっきと同じエラーが出るよ。目印の型は "inu" のように値そのもので書こう。

type Inu = {  shurui: string;  namae: string;};type Tori = {  shurui: string;  tobu: boolean;};function dasu(a: Inu | Tori): void {  if (a.shurui === "inu") {    console.log(a.namae);  }}

実行結果

型のエラー: プロパティ 'namae' は型 'Inu | Tori' に存在しません。
  プロパティ 'namae' は型 'Tori' に存在しません。

05 / 05

3つ以上でも同じ

つなぐメンバーが増えても、やることは変わらない。目印の値を1つずつちがう文字にして、else if で並べるだけだよ。

type で名前をつけておくと、引数にも配列の中身にも同じ型が使い回せる。ここから書いてみよう。目印を持たせて、if で見る、それだけだ。

type Ame = {  tenki: "ame";  ryou: number;};type Hare = {  tenki: "hare";  kion: number;};type Tenki = Ame | Hare;function dasu(t: Tenki): void {  if (t.tenki === "ame") {    console.log("あめ " + t.ryou);  } else {    console.log("はれ " + t.kion);  }}dasu({ tenki: "ame", ryou: 12 });dasu({ tenki: "hare", kion: 28 });

実行結果

あめ 12
はれ 28