0
0

Go | Union を Interface で実現する

Last updated at Posted at 2024-07-30

最近はもっぱら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)

Go で switch

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0