型で正しい状態の場合分けを列挙して表現する
以下の2つは同じ意味ではないよねという話
1.tstype X = {
a: A1 | A2;
b: B1 | B2
}
2.tstype X = { a: A1; b: B1 }
| { a: A2; b: B2 }
仕様として以下のようなものをが許容されないのであれば、 2.ts
のように定義すべき
tstype Y = {
a: A1
b: B2
}
A1
, A2
, B1
, B2
の取りうる値が1つだったとしても、前者は4パターン取りうる
テストのパターンも倍になる
型は仕様なので、それで表現した値は全部許容される、と明言したことになる
仕様にふさわしいのが 2.ts
なのであればそう書くべき
もちろん
1.ts
の方がふさわしい仕様の場合もある

例えば、以下のような仕様があるとする
Userは、必ずEmailか電話番号の情報を持っている
この仕様を満たすように型を定義するとどうなるか?
例えば、こう書ける
tstype User = {
name: UserName;
email: Email;
phone: PhoneNumber;
}
しかし、これだと両方必須になってしまうので、
例えばこう修正する
tstype User = {
name: UserName;
email: Email | undefined;
phone: PhoneNumber | undefined;
}
しかし、これも間違い。
この型の場合、emailとphoneの両方がundefinedなものも許容してしまう
tsconst user1: User = {
name: 'mrsekut',
email: undefined,
phone: undefined
}
型が仕様を正しく表現していない
例えば、こうする
tstype User = {
name: UserName;
email: Email;
phone: undefined;
} | {
name: UserName;
email: undefined;
phone: PhoneNumber
} | {
name: UserName;
email: Email;
phone: PhoneNumber;
}
場合分けは、 user.email != null
のように、「undefinedじゃない」という条件式でしか絞り込めない
あるいはこうする
tstype User = {
type: 'emailOnley';
name: UserName;
email: Email;
} | {
type: 'phoneOnly';
name: UserName;
phone: PhoneNumber
} | {
type: 'both';
name: UserName;
email: Email;
phone: PhoneNumber;
}
あるいはこう
userを丸ごとtagged unionにする必要がないので、Contact部分のみをそうする
tstype User = {
name: UserName;
contact: Contact;
}
type Contact = {
type: 'emailOnly';
email: Email
} | {
type: 'phoneOnly';
phone: PhoneNumber;
} | {
type: 'both';
email: Email;
phone: PhoneNumber;
}
Haskellならこうするかな
hsdata User = User
{ name :: UserName
, contact :: Contact
}
data Contact = EmailOnly Email
| PhoneOnly Phone
| Both BothContactMethods
data BothContactMethods = BothContactMethods Email Phone
data Email = Email String
data Phone = Phone String
参考
正しい状態(自体)をunionする
hsdata ContactInfo
= EmailOnly Email
| PostalOnly Postal
| Both Email Postal
仕様的に空になることがないなら、それも型で表現する
hstype Order = { items :: NonEmptyArray Item }