最近はもっぱらGoを書いてるがメンタルモデルがTypeScriptに引きづられていたのでコード例を交えて比較して書いてみる。
Go
- Playground
- Interfaceで実現する
- identifyなどのようにAやBごとのそれぞれの処理は、それぞれの構造体に対してメソッドを生やす形で実現する
- ダックタイピングなので、AとBはInterfaceのAorBのことを知らない。
AorB
のInterfaceを満たすものならA、B以外でも入れられる
package main
import (
"fmt"
)
func main() {
{
// A
c := ContainerItem{AorB: A{IamA: "なんかの値A"}}
c.Echo("A: ")
}
{
// B
c := ContainerItem{AorB: B{IamB: 123}}
c.Echo("B: ")
}
{
// Interface で隠蔽している構造体にアクセス
c := ContainerItem{AorB: B{IamB: 123}}
switch aorb := c.AorB.(type) {
case A:
fmt.Println("A: ", aorb.IamA)
case B:
fmt.Println("B: ", aorb.IamB)
}
}
{
// AorBList
src := AorBList{B{IamB: 123}, A{IamA: "なんかの値A"}}
cl := ContainerList{AorBList: src}
cl.IsSimilar(AorBList{B{}, A{}},)
}
}
// AorB
//
// A か B を表すInterface
type AorB interface{ Identify() string }
// A
//
// Aの実装
type A struct{ IamA string }
func (a A) Identify() string { return "This is A" }
// B
//
// B の実装
type B struct{ IamB int64 }
func (b B) Identify() string { return "This is B" }
// ContainerItem
//
// AorB を利用するような構造体
type ContainerItem struct{ AorB AorB }
func (c ContainerItem) Echo(str string) { fmt.Println(str, c.AorB.Identify()) }
// ContainerList
//
// AorB の配列を利用するような構造体
type AorBList []AorB
type ContainerList struct{ AorBList AorBList }
func (c ContainerList) IsSimilar(dsts AorBList) bool {
for i, src := range c.AorBList {
dst := dsts[i]
if src.Identify() != dst.Identify() {
return false
}
}
return true
}
TypeScript
- Playground
- TypeScript だと Unionに対する実装として switch を使うことが多い
- never を用いれば型分岐の漏れは防げるが、全てのswitchにneverを入れないといけないので実装漏れは起きやすい
- identifyなどのようにAやBごとのそれぞれの処理は、switchで分岐させたcaseのなかで書いたりそれぞれの無名関数を作っておいてcaseの中からその関数を呼ぶことが多い
- kind のように、AやBごとに違うが必ず値を持っているものはそれぞれの構造体のなかで値をもつ
type AorB = A | B
type A = {IamA: string, kind: "A"}
type B = {IamB: number, kind: "B"}
const identify = (aorb: AorB ): string => {
switch (aorb.kind) {
case "A":
return "This is A"
case "B":
return "This is B"
default:
const _: never = aorb;
return ":D"
}
}
const echo = (str: string, aorb: AorB): void => {
console.log(str, identify(aorb))
}
// main
const a = {IamA: "Aだよー", kind: "A" as const}
echo("A: ", a)
const b = {IamB: 1234, kind: "B" as const}
echo("B: ", b)