📦

Go で TypeScript みたいに implements をしたかった話

に公開

はじめに

この記事ではGoでTypeScriptのimplementsみたいにインターフェースを保証する方法について説明します。

TypeScriptのimplements

TypeScript ではClassを定義する際に、意図したプロパティやメソッドがすべて含まれていることを保証するために implements を使います。
interfaceで宣言したプロパティやメソッドを implementsしてクラスを定義することで、インターフェースを正しく実装しているかコンパイル時にチェックできます。

例:

interface Cat {
  name: string;
  age: number;
  color: string;
  meow(): void;
}

class MyCat implements Cat {
  name: string;
  age: number;
  color: string;

  constructor(name: string, age: number, color: string) {
    this.name = name;
    this.age = age;
    this.color = color;
  }

  meow(): void {
    console.log(`${this.name} is meowing!`);
  }
}

Go ではどうやってインターフェースを保証する?

Go にはimplementsという宣言はないですが、以下のように書くことで、コンパイル時にインターフェースの実装をチェックできます。

package main

import "fmt"

type Cat interface {
    Meow()
}

type MyCat struct {
    Name  string
    Age   int
    Color string
}

// ここがimplementsと同じようなチェックを実現しています!
var _ Cat = (*MyCat)(nil)

func (c *MyCat) Meow() {
    fmt.Printf("%s is meowing!\n", c.Name)
}

var _ Cat = (*MyCat)(nil) の行がポイントです。

仕組み

この1行で何が起きているのか見ていきましょう。

var _ Cat = (*MyCat)(nil)
  • (*MyCat)(nil)*MyCat 型の nil ポインタを作成
  • それを Cat インターフェース型の変数に代入しようとする
  • もし *MyCatCat インターフェースを実装していなければ、コンパイルエラーになる
  • _ (ブランク識別子) を使うことで、実際には変数を使わないことを示す

つまり、この1行があることで:

  • Meow() メソッドを実装し忘れた場合、コンパイル時にエラーが出る
  • TypeScriptの implements と同じように、インターフェースの実装を保証できる

まとめ

Go でインターフェースの実装をコンパイル時にチェックするには、以下のように書きます。

var _ InterfaceName = (*ConcreteType)(nil)

これにより、TypeScript の implements と同じように、コンパイル時に型の安全性を保証できます。

参考

Discussion